【问题标题】:What does "Code as Data" mean?“代码即数据”是什么意思?
【发布时间】:2015-02-01 19:44:55
【问题描述】:

我最近遇到了来自 EclipseCon 2014 的 presentation,在第 5 页上他们说 “Lambda 表达式允许您将代码视为数据”

我也遇到了这个示例代码

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent event) {
        System.out.println("button clicked");
    }
});

来自 Richard Warburton 的“Java 8 Lambdas: Pragmatic Functional Programming”

“这实际上是一个使用代码作为数据的例子——我们给出了 button 一个代表动作的对象。”

对于 Lambda 表达式和/或匿名内部类,code as data 是什么意思?

【问题讨论】:

  • 我猜它是指将方法作为值传递,而 lambdas 给人一种做事的错觉。值被视为数据,“代码”可能是指作为方法的代码块
  • 看起来像任何将函数视为主要类公民的函数式语言(将函数作为另一个函数的输入的概念)。这个例子对我来说看起来很乱,其他函数式或混合函数式的语言在语法上做得更好,例如检查 Scala(它仍然基于 JVM)。
  • “代码即数据”是一个标语,并不意味着您不知道任何事情。

标签: java lambda java-8 anonymous-inner-class


【解决方案1】:

当您将功能作为参数传递给另一个方法时,例如当有人单击您的示例代码之类的按钮时应该采取什么操作。

Lambda 表达式使您能够做到这一点,将功能视为方法参数 = code as data

有关更多信息和 lambda 代码示例,请参阅 oracle link

【讨论】:

    【解决方案2】:

    “代码即数据”的概念与具有一流功能的编程语言的概念密切相关。请参阅有关此主题的 Wikipedia article。在这篇other answer 中,我讨论了Java 是否具有一流的功能,但那篇文章也讨论了其他一些问题。

    维基百科文章中的定义(引用了 Abelson & Sussman,计算机程序的结构和解释,第 1.3 节)特别提到了使它们成为“一流”的函数的以下特征:

    • 可以作为参数传递给其他函数
    • 可以作为其他函数的值返回
    • 可以赋值给变量
    • 可以存储在数据结构中

    这些都是您对数据所做的事情。如果你可以用函数做同样的事情,那就像把“代码当作数据”一样。

    如果您仔细观察 lambda 是如何添加到 Java 编程语言中的,您会发现 lambda 确实被转换为函数式接口的实例。因此,它是某个对象的实例,因此是类Object 的后代,并且与Java 中的所有对象一样,它具有equals(), hashCode(), getClass() 等方法,并且可以将引用与== 等进行比较。但是,明确不鼓励您依赖任何这些。有关其他讨论,请参阅我的 other answer。在实践中,当您在 Java 中使用 lambda 时,感觉就像是在将代码作为参数传递,或者将其分配给变量。例如,

    list.replaceAll(x -> doSomething(x));
    
    Predicate<String> pred = s -> s.length() > 5;
    

    您真的最终没有想到,在幕后,lambda 是作为函数接口实例的对象。

    【讨论】:

      【解决方案3】:

      这意味着您编写的程序代码也是一个数据,可以作为参数传递给另一个method并由程序操作。

      【讨论】:

        【解决方案4】:

        在 Java 的黄金时代,没有方便的范例将功能从调用者传递给接收者。您必须使用某种方法(通常通过“Command”设计模式)组合一个类,然后将其传递给接收者。

        Java 8 Lambda 通过引入 Lambda 表达式改变了范式。忘记这些是如何呈现给运行时的,Lambda 对开发人员来说似乎是可以传递给方法的纯代码。代码即数据!

        这种范式转变为 java.util.Stream 铺平了道路,它接受 lambda 表达式,告诉集合执行一项功能。

        Maurice Naftalin 的新书Mastering Lambdas - Java Programming in a Multi-Core World 是对整个主题的清晰、简洁和完整的处理。

        【讨论】:

          【解决方案5】:

          代码永远是数据!程序的字节码指令存储在内存中。程序员没有直接读写这块内存的权限,但它就在那里。

          当我们谈论将代码“视为”数据时,我们指的是存储对该内存的引用,以便按照我们通常认为的“数据”的方式使用和组织内存。

          例如,可以想象在button 内部看起来像这样:

          class Button {
              private ActionListener[] listeners = new ActionListener[100];
              private int count = 0;
          
              public void addActionListener(ActionListener listener) {
                  listeners[count] = listener;
                  ++count;
              }
          
              // called somehow when the button is clicked
              void notifyListeners() {
                  ActionEvent theEvent = new ActionEvent(...);
                  for(int i = 0; i < count; ++i) {
                      listeners[i].actionPerformed(theEvent);
                  }
              }
          }
          

          基本上Button 将函数列表保存在数组中:这将代码视为数据。在大多数情况下,ActionListener 除了引用 actionPerformed 的特定覆盖之外没有其他用途。

          由于所有 Java 实例方法都是虚拟的,我们总是可以将代码视为数据。 AWT/Swing 精心设计的事件监听框架证明了这一点。 Lambda 表达式只是增加了概念上的强调和简短的语法。 (在大多数情况下,由于它们的实施,性能也有所提高。)

          当我们只使用一个对象来实现一个特定的方法而不是存储值时,Lambda 可以让我们更清楚地表达我们的意图。

          从技术上讲,这都是通过间接完成的。 ActionListener 不保存代码:相反,虚拟机以某种方式知道(我们不知道)它指向内存中包含指向代码的指针的特定结构。因此,某种对代码的引用是实际传递的内容,因此“代码视为数据”。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-01-03
            • 1970-01-01
            • 2012-06-29
            • 2016-03-28
            相关资源
            最近更新 更多