Henrik Paul
Henrik Paul

Reputation: 67703

Strange syntax for instantiating an inner class

I didn't imagine that I would encounter radically new syntax in Java anymore at this stage, but lo and behold, I just encountered something:

The exact context and what the code below should do is pretty irrelevant - it's there just to give some kind of context.

I'm trying to synthetically create an event in IT Mill Toolkit, so I wrote this kind of line:

buttonClick(new Button.ClickEvent(button));

But, Eclipse gives me the following error message:

No enclosing instance of type Button is accessible. Must qualify the allocation with an enclosing instance of type Button (e.g. x.new A() where x is an instance of Button).

When I rewrite the line above as follows, it doesn't complain anymore:

buttonClick(button.new ClickEvent(button)); // button instanceof Button

So, my question is: What does the latter syntax mean, exactly, and why doesn't the first snippet work? What is Java complaining about, and what's it doing in the second version?

Background info: Both Button and Button.ClickEvent are non-abstract public classes.

Upvotes: 43

Views: 17460

Answers (6)

toto_tico
toto_tico

Reputation: 19027

You actually can do that, but you have to declare ClickEvent as static inside Button, and then you shouldn't have any problem using you sintax:

buttonClick(new Button.ClickEvent(button));

Basically static makes the class ClickEvent belong directly to the class Button instead of a specific instance(i.e. new Button()) of Button.


Following @Jon Skeet example:

// Button.java
class Button
{

    public static class ClickEvent
    {
        public ClickEvent(Button b)
        {
            System.out.println("Instance: " + this.toString());
        }
    }
}

// Test.java
public class Test
{
    public static void main(String[] args)
    {
        Button button = new Button();
        buttonClick(new Button.ClickEvent(button));
    }

    public static void buttonClick (Button.ClickEvent ce) {
    }
}

Upvotes: 2

pgras
pgras

Reputation: 12770

Button.ClickEvent is a non-static inner class so an instance of this class can only exist enclosed in a instance of Button.

In your second code example you have an instance of Button and you create an instance of ClickEvent enclosed in this Button instance...

Upvotes: 10

diginoise
diginoise

Reputation: 7620

Your code would compile, had you typed

buttonClick(new Button().ClickEvent(button));

instead of

buttonClick(new Button.ClickEvent(button));

as a constructor is a method and when you call a method in Java you must pass the list of arguments, even when it is empty.

Upvotes: -1

starblue
starblue

Reputation: 56772

To avoid confusing yourself and fellow programmers with this rarely-used feature you can always make inner classes static.

In case a reference to the outer class is needed you can pass it explicitly in the constructor.

Upvotes: 3

Daniel Earwicker
Daniel Earwicker

Reputation: 116674

A non-static inner class in Java contains a hidden reference that points to an instance of the outer class it is declared in. So the error message you got originally is telling you that you cannot create a new instance of the inner class without also specifying an instance of the outer class for it to be attached to.

Perhaps the reason you haven't seen that syntax before is that inner classes are often allocated in a method of the outer class, where the compiler takes care of this automatically.

Upvotes: 9

Jon Skeet
Jon Skeet

Reputation: 1500575

Inner classes (like Button.ClickEvent) need a reference to an instance of the outer class (Button).

That syntax creates a new instance of Button.ClickEvent with its outer class reference set to the value of button.

Here's an example - ignore the lack of encapsulation etc, it's just for the purposes of demonstration:

class Outer
{
    String name;

    class Inner
    {
        void sayHi()
        {
            System.out.println("Outer name = " + name);
        }
    }
}

public class Test
{
    public static void main(String[] args)
    {
        Outer outer = new Outer();
        outer.name = "Fred";

        Outer.Inner inner = outer.new Inner();
        inner.sayHi();
    }
}

See section 8.1.3 of the spec for more about inner classes and enclosing instances.

Upvotes: 71

Related Questions