为什么Object和var变量的行为不同?

问题描述 投票:5回答:2

有人可以解释o2的行为吗?是由于编译器优化吗?它记录在JLS的某个地方吗?

public class Test {
    public static void main(String[] args) {
        Object o1 = new Object() {
            String getSomething() {
                return "AAA";
            }
        };
        // o1.getSomething(); // FAILS
        String methods1 = Arrays.toString(o1.getClass().getMethods());        
        var o2 = new Object() {
            String getSomething() {
                return "AAA";
            }
        };        
        o2.getSomething(); // OK        
        String methods2 = Arrays.toString(o2.getClass().getMethods());        
        System.out.println(methods1.equals(methods2));
    }    
}

产生的输出是

true

[UPDATE]

经过一些富有成效和有用的讨论,我认为我可以理解这种行为(如果我的假设是错误的,请发表评论。

首先,感谢@ user207421,他解释说Java编译器将o2的类型与RHS相同,这是:

  • 扩展Object
  • 具有getSomething方法

然后感谢@ Joachim Sauer指出了JLS中的适当位置。

一些其他相关的JLS引号:

[局部变量的类型是T相对于T提到的所有合成类型变量的向上投影(第4.10.5节。]

在确定变量的类型时,向上投影将应用于初始化程序的类型。如果初始化程序的类型包含捕获变量,则此投影将初始化程序的类型映射到不包含捕获变量的超类型。

尽管有可能允许变量的类型提及捕获变量,但通过将它们投影开来,我们强制执行一个吸引人的不变式,即捕获变量的范围永远不比包含捕获其类型的表达式的语句大。非正式地,捕获变量不能“泄漏”到后续语句中。

问题:在问题的背景下,我们能否说“捕获变量”也指getSomething()

最后,感谢@ Slaw指出getSomething被声明为包私有,因此getMethods没有返回它。

感谢任何评论/纠正。

java var type-inference java-10
2个回答
4
投票

Object没有方法getSomething。并且由于o1的类型为Object,所以编译器将不允许您调用o1.getSomething

对于o2,变量的类型是您在初始化期间创建的匿名内部类型。该类型具有getSomething方法,因此编译器将允许您调用它。

有趣的是,具有命名类型不能直接执行此操作。在o2的声明中没有使用任何类型名称来获得相同的效果,因为该类型是匿名的。

JLS 14.4.1 Local Variable Declarators and Types中定义。特别是这部分:

如果LocalVariableType为var,则当T被视为初始化表达式的类型时,将其视为未出现在赋值上下文中,因此为独立表达式(第15.2节)。

甚至在下面的示例中都可以看到:

var d = new Object() {};  // d has the type of the anonymous class

3
投票

JEP 286: Local-Variable Type Inference状态中表示为不可定义类型的部分:

匿名类类型不能命名,但是很容易理解的-它们只是类。允许变量具有匿名类类型引入了用于声明单例的有用速记本地类的实例。我们允许他们。

因此,考虑到创建了类实例,并且允许使用var调用的方法进行编译,并且推断作为匿名类,进一步允许该方法被调用。

规范的Local Variable Declarators and Type部分以及示例也将其作为附带说明:

var d = new Object() {};  // d has the type of the anonymous class

请注意,某些用var声明的变量不能用显式类型,因为变量的类型不可表示。

[另一方面,在第一个实例中,您尝试执行的操作看起来像Invoking a method of an anonymous class,由于o1的类型被推断为Object,而且没有进一步的方法,该操作失败了。 getSomething。如果您要调用方法getSomething并在那里进行编译,则可以使用

Object o1 = new Object() {
  String getSomething() {
    System.out.println("something happened");
    return "AAA";
  }
}.getSomething();
© www.soinside.com 2019 - 2024. All rights reserved.