Странный синтаксис для создания экземпляра внутреннего класса

Я не предполагал, что на этом этапе я больше столкнулся с радикально новым синтаксисом в Java, но вот, я просто что-то обнаружил:

Точный контекст и то, что должен сделать приведенный ниже код, довольно неуместен - он просто должен дать какой-то контекст.

Я пытаюсь синтетически создать событие в IT Mill Toolkit, поэтому я написал такую ​​строку:

buttonClick(new Button.ClickEvent(button));

Но Eclipse сообщает мне следующее сообщение об ошибке:

Нет доступного экземпляра типа Button. Должен квалифицировать распределение с помощью экземпляра типа Button (например, x.new A(), где x является экземпляром Button).

Когда я переписываю строку выше следующим образом, она больше не жалуется:

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

Итак, мой вопрос: Что означает последний синтаксис, и почему не работает первый фрагмент? Что такое жалоба Java и что она делает во второй версии?

Фоновая информация: Оба Button и Button.ClickEvent являются не абстрактными общедоступными классами.

Ответ 1

Внутренние классы (например, Button.ClickEvent) нуждаются в ссылке на экземпляр внешнего класса (Button).

Этот синтаксис создает новый экземпляр Button.ClickEvent с его ссылкой на внешний класс, установленным на значение Button.

Здесь пример - игнорировать отсутствие инкапсуляции и т.д., это просто для демонстрации:

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();
    }
}

Подробнее о внутренних классах и охватывающих экземплярах см. раздел 8.1.3 спецификации.

Ответ 2

Button.ClickEvent - нестатический внутренний класс, поэтому экземпляр этого класса может существовать только в экземпляре Button.

В вашем втором примере кода у вас есть экземпляр Button, и вы создаете экземпляр ClickEvent, заключенный в этот экземпляр Button...

Ответ 3

Нестатический внутренний класс в Java содержит скрытую ссылку, указывающую на экземпляр внешнего класса, в котором он объявлен. Таким образом, сообщение об ошибке, которое вы изначально рассказываете, говорит вам, что вы не можете создать новый экземпляр внутреннего класса без указания экземпляра внешнего класса для присоединения к нему.

Возможно, причина, по которой вы не видели этот синтаксис раньше, заключается в том, что внутренние классы часто выделяются в методе внешнего класса, где компилятор автоматически выполняет это.

Ответ 4

Чтобы не запутать себя и других программистов с помощью этой редко используемой функции, вы всегда можете сделать внутренние классы статическими.

Если требуется ссылка на внешний класс, вы можете передать его явно в конструкторе.

Ответ 5

Вы действительно можете сделать это, но вам нужно объявить ClickEvent как static внутри Button, а затем у вас не должно возникнуть проблемы с использованием вы sintax:

buttonClick(new Button.ClickEvent(button));

В основном static класс ClickEvent относится непосредственно к классу Button вместо конкретного экземпляра (т.е. new Button()) Button.


Следуя примеру @Jon Skeet:

// 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) {
    }
}

Ответ 6

Ваш код будет скомпилирован, если бы вы набрали

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

вместо

buttonClick(new Button.ClickEvent(button));

как конструктор - это метод, и когда вы вызываете метод в Java, вы должны передать список аргументов, даже когда он пуст.