Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do Object and var variables behave differently?

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:

  • extends Object
  • has the 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.

like image 388
Barat Sahdzijeu Avatar asked Dec 11 '19 09:12

Barat Sahdzijeu


People also ask

What are variables declared inside a class but not inside a method called?

Variables declared inside the class but outside the body of the method are called instance variables. It is called instance variable because its value is instance specific and is not shared among instances. Variables declared inside the body of a method are called local variables.

Why VAR is not used in Java?

In Java, var can be used only where type inference is desired; it cannot be used where a type is declared explicitly. If val were added, it too could be used only where type inference is used. The use of var or val in Java could not be used to control immutability if the type were declared explicitly.

Can methods use instance variables?

Things an object does are its methods (behavior). Methods can use instance variables so that objects of the same type can behave differently. A method can have parameters, which means you can pass one or more values in to the method.

How do you call a variable from another method in Java?

You can't. Variables defined inside a method are local to that method. If you want to share variables between methods, then you'll need to specify them as member variables of the class. Alternatively, you can pass them from one method to another as arguments (this isn't always applicable).


2 Answers

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
like image 148
Joachim Sauer Avatar answered Sep 30 '22 16:09

Joachim Sauer


The section represented as Non-Denotable Types in the JEP 286: Local-Variable Type Inference states:

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.

Hence the method that you invoke using the var is allowed to compile considering the class instance is created and inferred as an anonymous class further allowing the method to be invoked.

The Local Variable Declarators and Type section of the specification mentions this as a side note along with the example as well:

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.

On the other hand, with the first instance what you're trying to perform looks like Invoking a method of an anonymous class, which fails since the type of o1 is inferred to be Object and that further doesn't have a method called getSomething. While if you were to invoke the method getSomething and fix the compilation there, you could have used

Object o1 = new Object() {
  String getSomething() {
    System.out.println("something happened");
    return "AAA";
  }
}.getSomething();
like image 26
Naman Avatar answered Sep 30 '22 16:09

Naman