Edw*_*ard 23 java lambda overloading function
首先,我不知道如何恰当地表达这个问题,所以这是一个建议。
假设我们有以下重载方法:
void execute(Callable<Void> callable) {
try {
callable.call();
} catch (Exception e) {
e.printStackTrace();
}
}
<T> T execute(Supplier<T> supplier) {
return supplier.get();
}
void execute(Runnable runnable) {
runnable.run();
}
Run Code Online (Sandbox Code Playgroud)
从这张表出发,我从另一个问题中得到了答案
Supplier () -> x
Consumer x -> ()
BiConsumer x, y -> ()
Callable () -> x throws ex
Runnable () -> ()
Function x -> y
BiFunction x,y -> z
Predicate x -> boolean
UnaryOperator x1 -> x2
BinaryOperator x1,x2 -> x3
Run Code Online (Sandbox Code Playgroud)
以下是我在本地得到的结果:
Supplier () -> x
Consumer x -> ()
BiConsumer x, y -> ()
Callable () -> x throws ex
Runnable () -> ()
Function x -> y
BiFunction x,y -> z
Predicate x -> boolean
UnaryOperator x1 -> x2
BinaryOperator x1,x2 -> x3
Run Code Online (Sandbox Code Playgroud)
编译器如何知道要调用哪个方法?例如,它如何区分什么是 aCallable
和什么是 a Runnable
?
Pan*_*kos 17
我相信我已经找到了官方文档中描述这一点的地方,尽管有点难以阅读。
\n这里提到:
\n\n\n15.27.3。Lambda 表达式的类型
\n请注意,虽然严格调用上下文中不允许装箱,但始终允许对 lambda 结果表达式进行装箱 - 也就是说,结果表达式出现在赋值上下文中,而不管包含 lambda 表达式的上下文如何。但是,如果显式\n类型化 lambda 表达式是重载方法的参数,\n最具体的检查 (\xc2\xa715.12.2.5) 会首选避免装箱或拆箱 lambda 结果的方法签名。
\n
然后这里(15.12.2.5)分析性地描述了如何选择最具体的方法。
\n所以根据这个例如所描述的
\n\n\n对于使用参数表达式 e1, ..., ek 的调用,如果满足\n以下任一条件,则一种适用的方法 m1 比另一种适用的方法 m2 更具体:
\nm2 是通用的,对于参数表达式 e1, ..., ek,m1 被推断为比 m2 更具体
\n
所以
\n// Callable -> why is it not a Supplier?\nexecute(() -> null); <-- Callable shall be picked from 2 options as M2 is generic and M1 is inferred to be more specific\n\nvoid execute(Callable<Void> callable) { // <------ M1 \n try {\n callable.call();\n } catch (Exception e) {\n e.printStackTrace();\n }\n}\n\n\n <T> T execute(Supplier<T> supplier) { // <------ M2 is Generic\n return supplier.get();\n }\n
Run Code Online (Sandbox Code Playgroud)\n为什么 M1 被推断得更具体可以从这里描述的这个过程中追溯到(18.5.4 更具体的方法推断)
\n// Callable -> why is it not a Supplier? It does not throw any exceptions..\nexecute(() -> null);\n
Run Code Online (Sandbox Code Playgroud)\n这是因为Callable<Void>
方法和Supplier<T>
方法都适用,但前者更具体。您可以看到,只有两种方法之一就是这种情况,并且execute(() -> null);
将调用该方法。
为了表明它execute(Callable<Void>)
比 更具体execute(Supplier<T>)
,我们必须转到\xc2\xa718.5.4,因为后者是通用方法。
\n\n设 m1 为第一种方法,m2 为第二种方法。其中 m2 具有类型参数 P1, ..., Pp,令 \xce\xb11, ..., \xce\xb1p 为推理变量,并令 \xce\xb8 为替换 [P1:=\xce\xb11, . .., Pp:=\xce\xb1p]。
\n令 e1, ..., ek 为相应调用的参数表达式。然后:
\n\n
\n- 如果 m1 和 m2 可通过严格或松散调用应用(\xc2\xa715.12.2.2, \xc2\xa715.12.2.3),则令 S1, ..., Sk 为 m1 的形式参数类型,并令T1, ..., Tk 是 \xce\xb8 应用于 m2 的形式参数类型的结果。
\n- [...]
\n
如此,m1
也是如此。是。对于调用,is ,并且被推断为 is ,所以is 。就是那么。是execute(Callable<Void>)
m2
execute(Supplier<T>)
P1
T
execute(() -> null);
e1
() -> null
T
Object
\xce\xb11
Object
T1
Supplier<Object>
S1
Callable<Void>
。
现在仅引用与问题相关的部分:
\n\n\n判断m1是否比m2更具体的过程如下:
\n\n
\n- \n
首先,根据 \xc2\xa718.1.3 中指定的 P1, ..., Pp 的声明边界构造初始边界集 B。
\n- \n
其次,对于所有 i (1 \xe2\x89\xa4 i \xe2\x89\xa4 k),生成一组约束公式或边界。
\n否则,Ti 是功能接口 I 的参数化。必须确定 Si 是否满足以下五个条件:
\n[...]
\n如果五个条件全部成立,则生成以下约束公式或边界(其中 U1 ... Uk 和 R1 是捕获 Si 的函数类型的参数类型和返回类型,V1 ... Vk 和 R2分别是Ti的函数类型的参数类型和返回类型):
\n\n
\n- 如果 ei 是显式类型化 lambda 表达式:\n
\n\n
\n- [...]
\n- 否则,\xe2\x80\xb9R1 <: R2\xe2\x80\xba。
\n
请注意,不带参数的 lambda 是显式类型化 lambda。
\n将其应用回您的问题R1
is Void
,R2
is Object
,并且约束\xe2\x80\xb9R1 <: R2\xe2\x80\xba
表示Void
(不是小写void
)是 的子类型Object
,这是正确的且不矛盾。
最后:
\n\n\n第四,将生成的边界和约束公式减少并与 B 合并以产生边界集 B'。
\n如果 B\' 不包含绑定 false,并且 B\' 中的所有推理变量解析成功,则 m1 比 m2 更具体。
\n
由于约束\xe2\x80\xb9Void <: Object\xe2\x80\xba
并不矛盾,因此没有false
约束,因此execute(Callable<Void>)
比 更具体execute(Supplier<T>)
。
// Supplier -> this returns an Object, but how is that different from returning null?\nexecute(() -> new Object());\n
Run Code Online (Sandbox Code Playgroud)\n在这种情况下,就只能采用Supplier<T>
该方法了。期望您返回与 兼容的东西,而不是。Callable<Void>
Void
Object
// Callable -> because it can throw an exception, right?\nexecute(() -> {throw new Exception();});\n
Run Code Online (Sandbox Code Playgroud)\n不完全的。抛出异常使Callable<Void>
重载适用,但Runnable
重载仍然适用。选择前者的原因仍然是因为比表达式Callable<Void>
更具体(仅相关部分):Runnable
() -> { throw new Exception(); }
\n\n对于表达式 e,如果 T 不是 S 的子类型并且以下条件之一为真,则函数接口类型 S 比函数接口类型 T 更具体(其中 U1 ... Uk 和 R1 是S捕获的函数类型,V1...Vk和R2是T的函数类型的参数类型和返回类型):
\n\n
\n- 如果 e 是显式类型化 lambda 表达式 (\xc2\xa715.27.1),则以下条件之一为 true:\n
\n\n
\n- R2 是
\nvoid
。
基本上,对于显式类型化的 lambda,任何非void
返回函数接口类型都比void
返回函数接口类型更具体。
归档时间: |
|
查看次数: |
2142 次 |
最近记录: |