"Code as Data" 是什么意思?

What does "Code as Data" mean?

我最近在 EclipseCon 2014 上遇到了 presentation,在第 5 页他们说 "Lambda expressions allow you to treat code as data" .

我也遇到了这个示例代码

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

来自 Richard Warburton 的 "Java 8 Lambdas: Pragmatic Functional Programming" 他说

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

关于 Lambda 表达式 code as data 是什么意思 and/or 匿名内部 class?

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

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

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

意思是你写的程序代码也是一个数据可以作为参数传递给另一个method 并由程序操纵。

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

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

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

莫里斯·纳夫塔林 (Maurice Naftalin) 的新书 Mastering Lambdas - Java Programming in a Multi-Core World 对整个主题进行了清晰、简洁和完整的处理。

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

当我们谈论 "treating" 代码作为数据时,我们指的是以我们通常认为的 "data" 的方式使用和组织的方式存储对该内存的引用。 =16=]

例如,人们可能会想象 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 不保存代码:相反,虚拟机以某种方式知道(我们不知道)它指向内存中保存指向代码的指针的特定结构。因此,对代码的某种引用实际上是在传递,因此“代码视为数据”。

"code as data" 的想法与具有 first-class 函数的编程语言的概念密切相关。看Wikipedia article on this topic. In this other answer我说的是Java是否有first-class功能,但是那篇文章也讨论了其他一些问题。

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

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

这些都是您对数据所做的事情。如果你可以用函数做同样的事情,那就像是对待 "code as data."

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

list.replaceAll(x -> doSomething(x));

Predicate<String> pred = s -> s.length() > 5;

你真的没有想到,在幕后,lambda 是函数接口实例的对象。