为什么必须为方法引用显式指定类/对象名?

Tag*_*eev 6 java java-8 method-reference

当我想引用当前作用域中的方法时,我仍然需要指定类名(对于静态方法)或者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());
    }
}
Run Code Online (Sandbox Code Playgroud)

这不是一个大问题this,但有时看起来过于拥挤静态方法,因为类名可能很长.如果编译器允许我简单地编写它会很好::trimmedLength:

public static void main(String[] args) {
    System.out.println(Stream.of("  aaa  ", "  bb ", " c ")
            .mapToInt(::trimmedLength).sum());
}
Run Code Online (Sandbox Code Playgroud)

但是Java-8编译器不允许这样做.对我来说,如果类/对象名称的解析方式与普通方法调用相同,那么它似乎会非常一致.这也将支持方法引用的静态导入,这在某些情况下也很有用.

所以问题是为什么在Java 8中没有实现这样或类似的语法?这种语法会出现什么问题吗?或者根本不考虑它?

Hol*_*ger 6

我不能代表Java开发人员,但有一些事情需要考虑:

有某种方法参考:

  1. 参考静态方法,例如 ContainingClass::staticMethodName
  2. 引用特定对象的实例方法,例如 containingObject::instanceMethodName
  3. 引用特定类型的任意对象的实例方法,例如 ContainingType::methodName
  4. 引用构造函数,例如 ClassName::new

编译器必须做一些工作来消除表单1和3的歧义,有时它会失败.如果表单::methodName被允许,编译器必须消除三种不同形式之间的歧义,因为它可以是从1到3的三种形式中的任何一种.

也就是说,允许表单::methodName快捷地删除表单1到表单3中的任何表单仍然不会暗示它等同于methodName(…)表达式可能引用的表单simpleName ( argopt )

  • 当前类或其超类和接口范围内的实例方法
  • 一个static在当前类或其超范围方法
  • 外部类或其超类和接口范围内的实例方法
  • 一个static在外部类或其超范围方法
  • static通过声明的方法import static

所以说" ::name应该被允许参考任何方法name(…)可以参考"意味着结合这两个列表的可能性,你应该在做出愿望之前三思而后行.


作为最后一点,您仍然可以选择编写一个lambda表达式args -> name(args),这意味着解析name就像对表单的简单方法调用name(args)一样,同时解决模糊问题,因为它消除了方法引用类型的选项3,除非你明确写(arg1, otherargs) -> arg1.name(otherargs).

  • 对于任何人来说,@ Brian Goetz都有一个回应:http://stackoverflow.com/questions/31665393/thismethodreference-or-equivalent-in-static-init-block-in-java-8#comment51275586_31665393 (6认同)
  • @Mrinal K. Samanta:不仅编译器必须消除歧义.人类读者也必须这样做.而对方法目标的错误想法将导致更多的伤害,而不是键入少量字母可以补偿. (2认同)