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

Why do Object and var variables behave differently?

有人可以解释 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

[更新]

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

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

然后感谢 @Joachim Sauer 他指出了 JLS 中的正确位置。

更多相关的 JLS 引用:

The type of the local variable is the upward projection of T with respect to all synthetic type variables mentioned by T (§4.10.5).

Upward projection is applied to the type of the initializer when determining the type of the variable. If the type of the initializer contains capture variables, this projection maps the type of the initializer to a supertype that does not contain capture variables.

While it would be possible to allow the type of the variable to mention capture variables, by projecting them away we enforce an attractive invariant that the scope of a capture variable is never larger than the statement containing the expression whose type is captured. Informally, capture variables cannot "leak" into subsequent statements.

问题:我们可以说 "capture variables" 在问题的上下文中也指代 getSomething() 吗?

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

任何comments/corrections赞赏。

Object 没有方法 getSomething。由于 o1Object 类型,编译器不允许您调用 o1.getSomething.

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

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

JLS 14.4.1 Local Variable Declarators and Types中定义。具体这部分:

If LocalVariableType is var, then let T be the type of the initializer expression when treated as if it did not appear in an assignment context, and were thus a standalone expression (§15.2).

下面甚至还有一个例子可以说明这一点:

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

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

Anonymous class types cannot be named, but they're easily understood—they're just classes. Allowing variables to have anonymous class types introduces a useful shorthand for declaring a singleton instance of a local class. We allow them.

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

规范的 Local Variable Declarators and Type 部分在示例中也提到了这一点:

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

Note that some variables declared with var cannot be declared with an explicit type, because the type of the variable is not denotable.

另一方面,您尝试执行的第一个实例看起来像 ,由于 o1 的类型被推断为 Object 并且失败了进一步没有名为 getSomething 的方法。如果你要调用方法 getSomething 并在那里修复编译,你可以使用

Object o1 = new Object() {
  String getSomething() {
    System.out.println("something happened");
    return "AAA";
  }
}.getSomething();