提问人:J. Mini 提问时间:8/22/2021 最后编辑:J. Mini 更新时间:8/27/2021 访问量:404
在一类元素的定义中,Java 的函数满足了多少个点?
How many of these points in the definition of a first-class element do Java's functions satisfy?
问:
计算机程序的结构和解释给出了以下条件,即编程语言的元素必须满足才能被认为是一流的:
它们可以按变量命名。
它们可以作为参数传递给过程。
它们可以作为程序的结果返回。
它们可能包含在数据结构中
Java 的函数满足了其中的多少个?如果有任何歧义,例如“将函数放在对象中是否算作 #4?”,请在您的回答中提及。
答:
Java 中的函数没有定义的类型。我们可以写类似 的东西,但从本质上讲,这只是 或 的语法糖,它将产生一个函数接口的实例(分别参见 JLS,§15.13 和 §15.27)。因此,这些方法始终包装在(可能是匿名的)接口实现中。Objects::nonNull
(foo) -> Objects.nonNull(foo)
如果我们将其与C++等语言进行比较,其中每个函数都有一个定义的类型,我们会看到Java lambda不满足任何这些标准。
评论
javap
javap
javap
这是值得商榷的。当然,你可以写这样的东西:
Function<Integer, Integer> times2 = x -> x * 2;
这是用变量命名函数吗?嗯,是也不是。该变量包含对“函数”的引用,因此“函数”是一个值,由变量命名。另一方面,Java 语言规范是这样说的:times2
接口类型的变量可以保存 null 引用或对实现接口的任何类的任何实例的引用。
也就是说,变量实际上并不直接保存对函数的引用;它包含对具有方法的对象的引用。JLS 不允许它保存“函数”或“对函数的引用”,只允许“对实例的引用”,这满足接口。该实例有一个名为 的方法,该方法在调用时执行该函数。apply
apply
我们能说对象就是函数吗?嗯,是也不是,这是一个解释问题。但我倾向于“否”,因为方法的名称特定于接口,如果您在不同的上下文中编写相同的 lambda 函数,那么“函数对象”可以具有不同名称的方法。如果同一个 lambda 函数是在两个不同的上下文中编写的,它是“同一个函数”吗?我认为是的,但显然它们不是相同的对象,因为它们具有不同的方法名称。所以我得出结论,如果它们是相同的函数但不是同一个对象,那么函数和对象就是不同的东西。apply
Function
请注意,@rzwitserloot为相反的解释辩护的答案也是正确的,除非他们声称他们的解释是唯一可能的解释。正如我所说,这是值得商榷的。我应该认为我们正在辩论的事实足以证明这个命题......
评论
times2
times2
times2
System.identityHashCode
times2
Function<Integer, Integer>
Function<Integer, Integer>
null
List<Integer>
List<Integer>
null
Integer
Integer
Integer
Integer
null
Function<Integer, Integer> x = ...; synchronized (x) {}
if (x)
Function<Integer, Integer>
null
- 它们可以按变量命名。
是的,很明显:
Runnable r = () -> System.out.println("Hello");
Runnable r2 = System.out::println;
我有函数/方法引用,我可以有引用它们的命名变量。
多义性:
这些变量也可以指向实际对象:
r = new Runnable() {
public void run() {
System.out.println("Goodbye!");
}
};
与函数语法示例不同,上述 means 实际上是指向具有定义的类似对象特征的确定对象。内存中有一个真实的对象,java lang 规范保证了这一点。r
但是,这并不重要。在像 javascript 或 python 这样的语言中,我可以将任何东西分配给变量(变量在这些语言中是非类型的)。这并不能仅仅因为我可以将 和 分配给任何变量而“使所有变量都布尔”。true
false
同样,在 java 中,要调用函数,例如实际打印给定的:,我必须编写而不是 .Hello
Runnable r = () -> System.out.println("Hello");
r.run()
r()
这是关于语法的争论,它不是基本的东西。在 python 中,将接受变量 ,将取消引用它(跟随指针),尝试将它在那里找到的内容解释为函数,然后执行它,不传递任何参数。如果无法强制执行函数,则会发生错误。r()
r
Java 也不例外。 将接受变量,将取消引用它(跟随指针),尝试将它在那里找到的内容解释为 Runnable 类型的函数,并执行它,不传递任何参数。如果它发现那里没有 Runnable(例如,它是 ),则会发生错误。r.run()
r
null
看?相同。试图用其他语言的做事方式来定义事物会错误地导致人们说,不知何故,上面是“不算数”的“语法糖”,这是一个错误的结论。
- 它们可以作为参数传递给过程。
是的,毫不含糊。
public void runTwice(Runnable r) {
r.run();
r.run();
}
Runnable r = () -> System.out.println("Hello");
runTwice(r);
runTwice(System.out::println);
runTwice(() -> System.out.println("Goodbye!"));
我正在将这些函数传递给 java。
- 它们可以作为程序的结果返回。
是的,毫不含糊:
public Runnable printMeTwice(String text) {
return () -> {
System.out.println(text);
System.out.println(text);
};
}
Runnable r = printMeTwice();
r.run();
- 它们可能包含在数据结构中
是的,毫不含糊:
class Animal {
String name;
Function<Food, Excrement> eat;
}
Animal animal = new Animal();
animal.eat = food -> return food.extractNutrients();
如果有任何歧义。
使用粘贴时的特定措辞,根本没有歧义,这有点有趣,考虑到这个问题已经有答案,要么被误解,要么对这些词使用特别奇怪的解释。
与大多数其他语言相比,java 有点不同的一个“奇怪”之处在于,在大多数语言中,如果变量包含一个函数,要完成“取消引用指针,然后执行你这样做时找到的函数”的工作,你会写 .在 java 中,你不会这样做;相反,在 Java 中,你做 or ;在 Java 中,函数具有命名类型,而在大多数语言中则没有。例如,在 java 中,我可以有“仅整数计算器操作”的概念:r
r()
r.run()
r.apply(t)
IntCalcOp plusButton = (a, b) -> a + b;
我可以有一个“整数比较函数”的概念:
IntComparator highestIsEarlier = (a, b) -> b - a;
这两件事根本不同。我无法将类型的变量传递给需要 IntComparator 的方法,即使签名在功能上完全相同 - 两者都接受 2 个 int 并返回一个 int。IntCalcOp
请注意,这并非天生就低人一等;事实上,它可能天生就优越。这是语言设计中的意见分歧。为了展示名义类型和完全没有隐式转换的好处,这里有 2 个非常简单的对象定义,它们在功能上是相同的:
interface Camera {
void shoot(Person p);
}
interface Gun {
void shoot(Person p);
}
在一些语言中,比如 python 和 javascript,如果我给你一把枪,而你期望有一台相机,你就会杀人。在 Java 中,这是不可能的。
再多的喋喋不休的“但它是语法糖”也改变了上面 4 个片段中的任何一个,这些片段清楚地表明,根据计算机程序的结构和解释的定义,Java 的函数是第一类元素。
我不认为那本书有一点脚注说:“语法糖不算数”。如果你愿意在有这个警告的情况下进行操作,我们首先需要定义语法糖的含义。*
例如,java 语言规范并没有说这些被转换为匿名的内部类文本。实现甚至不再这样做(其他答案提到了这一点,并且在这方面是不正确的)。
C 编译器的工作方式可能是首先发出汇编程序代码,然后通过汇编程序运行该代码以生成可执行文件。这是否使所有 C 代码都只是“语法糖”?如果这是真的,那么 C 甚至没有循环。
这种推理很有趣,但我认为,为了确定一种语言能做什么,完全没有意义。从本质上讲,如果你挖掘得足够深入,所有编程语言都是 100% 的语法糖。
评论
Objects::nonNull
someObject.getClass()::isInstance
(foo) -> Objects.nonNull(foo)
var
o -> Objects.nonNull(o)
new
Objects::nonNull
Objects::nonNull