为什么必须为方法引用显式指定 class/object 名称?
Why class/object name must be explicitly specified for method references?
当我想引用当前范围内的方法时还需要
在 ::
之前指定 class 名称(对于静态方法)或 this
操作员。例如,我需要写:
import java.util.stream.Stream;
public class StreamTest {
public static int trimmedLength(String s) {
return s.trim().length();
}
public static void main(String[] args) {
System.out.println(Stream.of(" aaa ", " bb ", " c ")
.mapToInt(StreamTest::trimmedLength).sum());
}
}
对于 this
来说这不是什么大问题,但有时静态方法看起来过于拥挤,因为 class 名称可能很长。如果编译器允许我简单地写 ::trimmedLength
就好了:
public static void main(String[] args) {
System.out.println(Stream.of(" aaa ", " bb ", " c ")
.mapToInt(::trimmedLength).sum());
}
但是Java-8 编译器不允许这样做。对我来说,如果 class/object name 以与正常方法调用相同的方式解析,这似乎是非常一致的。这也将支持方法引用的静态导入,这在某些情况下也很有用。
所以问题是为什么 Java8 中没有实现这样或类似的语法?这种语法会出现什么问题吗?还是根本就没有考虑过?
我不能代表 Java 开发人员,但有一些事情需要考虑:
有一定的kind of method references:
- 引用静态方法,例如
ContainingClass::staticMethodName
- 引用特定对象的实例方法,例如
containingObject::instanceMethodName
- 引用特定类型的任意对象的实例方法,例如
ContainingType::methodName
- 对构造函数的引用,例如
ClassName::new
编译器已经做了一些工作来消除形式 1 和 3 以及 sometimes it fails 的歧义。如果允许 ::methodName
形式,编译器必须消除三种不同形式之间的歧义,因为它可能是从 1 到 3 的三种形式中的任何一种。
也就是说,允许形式 ::methodName
简化形式 1 到 3 中的任何一个仍然不意味着它等同于形式 methodName(…)
作为表达式 simpleName ( arg<sub>opt</sub> )
可参考
- 当前class或其超classes和接口
范围内的实例方法
- 当前class或其超classes
范围内的static
方法
- 外部class或其超classes和接口
范围内的实例方法
- 一个
static
方法在外部class或其超classes 范围内
- 通过
import static
声明的 static
方法
所以说“::name
应该被允许引用任何方法 name(…)
可能会引用”暗示结合这两个列表的可能性,你应该三思而后行.
作为最后一点,您仍然可以选择编写像 args -> name(args)
这样的 lambda 表达式,这意味着解析 name
就像形式 name(args)
的简单方法调用,而在同时解决歧义问题,因为它消除了方法引用类型的选项 3,除非你明确地写 (arg1, otherargs) -> arg1.name(otherargs)
.
当我想引用当前范围内的方法时还需要
在 ::
之前指定 class 名称(对于静态方法)或 this
操作员。例如,我需要写:
import java.util.stream.Stream;
public class StreamTest {
public static int trimmedLength(String s) {
return s.trim().length();
}
public static void main(String[] args) {
System.out.println(Stream.of(" aaa ", " bb ", " c ")
.mapToInt(StreamTest::trimmedLength).sum());
}
}
对于 this
来说这不是什么大问题,但有时静态方法看起来过于拥挤,因为 class 名称可能很长。如果编译器允许我简单地写 ::trimmedLength
就好了:
public static void main(String[] args) {
System.out.println(Stream.of(" aaa ", " bb ", " c ")
.mapToInt(::trimmedLength).sum());
}
但是Java-8 编译器不允许这样做。对我来说,如果 class/object name 以与正常方法调用相同的方式解析,这似乎是非常一致的。这也将支持方法引用的静态导入,这在某些情况下也很有用。
所以问题是为什么 Java8 中没有实现这样或类似的语法?这种语法会出现什么问题吗?还是根本就没有考虑过?
我不能代表 Java 开发人员,但有一些事情需要考虑:
有一定的kind of method references:
- 引用静态方法,例如
ContainingClass::staticMethodName
- 引用特定对象的实例方法,例如
containingObject::instanceMethodName
- 引用特定类型的任意对象的实例方法,例如
ContainingType::methodName
- 对构造函数的引用,例如
ClassName::new
编译器已经做了一些工作来消除形式 1 和 3 以及 sometimes it fails 的歧义。如果允许 ::methodName
形式,编译器必须消除三种不同形式之间的歧义,因为它可能是从 1 到 3 的三种形式中的任何一种。
也就是说,允许形式 ::methodName
简化形式 1 到 3 中的任何一个仍然不意味着它等同于形式 methodName(…)
作为表达式 simpleName ( arg<sub>opt</sub> )
可参考
- 当前class或其超classes和接口 范围内的实例方法
- 当前class或其超classes 范围内的
- 外部class或其超classes和接口 范围内的实例方法
- 一个
static
方法在外部class或其超classes 范围内
- 通过
import static
声明的
static
方法
static
方法
所以说“::name
应该被允许引用任何方法 name(…)
可能会引用”暗示结合这两个列表的可能性,你应该三思而后行.
作为最后一点,您仍然可以选择编写像 args -> name(args)
这样的 lambda 表达式,这意味着解析 name
就像形式 name(args)
的简单方法调用,而在同时解决歧义问题,因为它消除了方法引用类型的选项 3,除非你明确地写 (arg1, otherargs) -> arg1.name(otherargs)
.