Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does `someObject.new` do in Java?

Tags:

java

In Java, I have just found out that the following code is legal:

KnockKnockServer newServer = new KnockKnockServer();                    
KnockKnockServer.receiver receive = newServer.new receiver(clientSocket);

FYI, receiver is just a helper class with the following signature:

public class receiver extends Thread {  /* code_inside */  }

I've never seen the XYZ.new notation before. How does that work? Is there any way to code that more conventionally?

like image 667
Caffeinated Avatar asked Mar 30 '13 23:03

Caffeinated


3 Answers

It's the way to instantiate a non-static inner class from outside the containing class body, as described in the Oracle docs.

Every inner class instance is associated with an instance of its containing class. When you new an inner class from within its containing class it uses the this instance of the container by default:

public class Foo {
  int val;
  public Foo(int v) { val = v; }

  class Bar {
    public void printVal() {
      // this is the val belonging to our containing instance
      System.out.println(val);
    }
  }

  public Bar createBar() {
    return new Bar(); // equivalent of this.new Bar()
  }
}

But if you want to create an instance of Bar outside Foo, or associate a new instance with a containing instance other than this then you have to use the prefix notation.

Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal(); // prints 5
like image 56
Ian Roberts Avatar answered Oct 24 '22 17:10

Ian Roberts


Have a look at this example:

public class Test {

    class TestInner{

    }

    public TestInner method(){
        return new TestInner();
    }

    public static void main(String[] args) throws Exception{
        Test t = new Test();
        Test.TestInner ti = t.new TestInner();
    }
}

Using javap we can view instructions generated for this code

Main method:

public static void main(java.lang.String[])   throws java.lang.Exception;
  Code:
   0:   new     #2; //class Test
   3:   dup
   4:   invokespecial   #3; //Method "<init>":()V
   7:   astore_1
   8:   new     #4; //class Test$TestInner
   11:  dup
   12:  aload_1
   13:  dup
   14:  invokevirtual   #5; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   17:  pop
   18:  invokespecial   #6; //Method Test$TestInner."<init>":(LTest;)V
   21:  astore_2
   22:  return
}

Inner class constructor:

Test$TestInner(Test);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #1; //Field this$0:LTest;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   return

}

Everything is simple - when invoking TestInner constructor, java passes Test instance as a first argument main:12. Not looking at that TestInner should have a no argument constructor. TestInner in its turn just saves reference to parent object, Test$TestInner:2. When you are invoking inner class constructor from an instance method, reference to parent object is passes automatically, so you do not have to specify it. Actually its passes every time, but when invoking from outside it should be passed explicitly.

t.new TestInner(); - is just a way to specify the first hidden argument to TestInner constructor, not a type

method() is equal to:

public TestInner method(){
    return this.new TestInner();
}

TestInner is equal to:

class TestInner{
    private Test this$0;

    TestInner(Test parent){
        this.this$0 = parent;
    }
}
like image 38
Mikhail Avatar answered Oct 24 '22 19:10

Mikhail


When inner classes were added to Java in version 1.1 of the language they were originally defined as a transformation to 1.0 compatible code. If you look at an example of this transformation, I think it will make it a lot clearer how an inner class actually works.

Consider the code from Ian Roberts' answer:

public class Foo {
  int val;
  public Foo(int v) { val = v; }

  class Bar {
    public void printVal() {
      System.out.println(val);
    }
  }

  public Bar createBar() {
    return new Bar();
  }
}

When transformed to 1.0 compatible code, that inner class Bar would become something like this:

class Foo$Bar {
  private Foo this$0;

  Foo$Bar(Foo outerThis) {
    this.this$0 = outerThis;
  }

  public void printVal() {
    System.out.println(this$0.val);
  }
}

The inner class name is prefixed with the outer class name so as to make it unique. A hidden private this$0 member is added that holds a copy of the outer this. And a hidden constructor is created to initialise that member.

And if you look at the createBar method, it would be transformed into something like this:

public Foo$Bar createBar() {
  return new Foo$Bar(this);
}

So let's see what happens when you execute the following code.

Foo f = new Foo(5);
Foo.Bar b = f.createBar();                               
b.printVal();

First we instantiate an instance of Foo and intialise the val member to 5 (i.e. f.val = 5).

Next we call f.createBar(), which instantiates an instance of Foo$Bar and initialises the this$0 member to the value of this passed in from createBar (i.e. b.this$0 = f).

Finally we call b.printVal() which tries to print b.this$0.val which is f.val which is 5.

Now that was a regular instantiation of an inner class. Let's look at what happens when instantiating Bar from outside Foo.

Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal();

Applying our 1.0 transformation again, that second line would become something like this:

Foo$Bar b = new Foo$Bar(f);

This is almost identical to the f.createBar() call. Again we're instantiating an instance of Foo$Bar and initialising the this$0 member to f. So again, b.this$0 = f.

And again when you call b.printVal(), you are printing b.thi$0.val which is f.val which is 5.

The key thing to remember is that the inner class has a hidden member holding a copy of this from the outer class. When you instantiate an inner class from within the outer class, it it implicitly initialised with the current value of this. When you instantiate the inner class from outside the outer class, you explicitly specify which instance of the outer class to use, via the prefix on the new keyword.

like image 7
James Holderness Avatar answered Oct 24 '22 18:10

James Holderness