Can someone explain the behavior of o2
? Is it due to compiler optimization? Is it documented somewhere in the 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));
}
}
The output produced is
true
[UPDATE]
After some productive and useful discussion I think I can understand the behavior (please post comments if my assumption is wrong).
First off, thanks to @user207421 who explained that the Java compiler treats the type of o2
the same as the RHS, which:
Object
getSomething
method Then thanks to @Joachim Sauer who pointed to the proper place in the JLS.
Some more related JLS quotes:
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.
Question: can we say that "capture variables" refers to getSomething()
too in the context of the question?
And finally, thanks to @Slaw for pointing out that getSomething
was declared package private so was not returned by getMethods
.
Any comments/corrections appreciated.
Object
has no method getSomething
. And since o1
is of type Object
the compiler won't allow you to call o1.getSomething
.
In the case of o2
the type of the variable is the anonymous inner type that you created during initialization. That type has a getSomething
method, so the compiler will allow you to call it.
Interestingly this is something that you can't directly do by having a named type. There's no type name that you use in the declaration of o2
to get the same effect, because the type is anonymous.
It is defined in JLS 14.4.1 Local Variable Declarators and Types. Specifically this part:
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).
There's even an example that shows that below:
var d = new Object() {}; // d has the type of the anonymous class