Странный синтаксис для создания экземпляров внутреннего класса
Я не предполагал, что на этом этапе я столкнусь с принципиально новым синтаксисом в Java, но вот, я только что столкнулся с чем-то:
Точный контекст и то, что должен делать код, приведенный ниже, не имеет значения - он просто дает некоторый контекст.
Я пытаюсь синтетически создать событие в IT Mill Toolkit, поэтому я написал такую строку:
buttonClick(new Button.ClickEvent(button));
Но Eclipse выдает мне следующее сообщение об ошибке:
Нет включающего экземпляра типа Button. Необходимо квалифицировать выделение с включающим экземпляром типа Button (например, xnew A(), где x - это экземпляр Button).
Когда я переписываю вышеприведенную строку следующим образом, она больше не жалуется:
buttonClick(button.new ClickEvent(button)); // button instanceof Button
Итак, мой вопрос: что конкретно означает последний синтаксис и почему не работает первый фрагмент? На что жалуется Java и что она делает во второй версии?
Справочная информация: оба Button
а также Button.ClickEvent
неабстрактные публичные классы.
6 ответов
Внутренние классы (как 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 спецификации для получения дополнительной информации о внутренних классах и включающих их экземплярах.
Button.ClickEvent является нестатическим внутренним классом, поэтому экземпляр этого класса может существовать только в экземпляре Button.
Во втором примере кода у вас есть экземпляр Button, и вы создаете экземпляр ClickEvent, заключенный в этот экземпляр Button...
Нестатический внутренний класс в Java содержит скрытую ссылку, которая указывает на экземпляр внешнего класса, в котором он объявлен. Таким образом, сообщение об ошибке, которое вы получили изначально, говорит вам, что вы не можете создать новый экземпляр внутреннего класса, не указав также экземпляр внешнего класса, к которому он должен быть присоединен.
Возможно, причина того, что вы еще не видели этот синтаксис, заключается в том, что внутренние классы часто размещаются в методе внешнего класса, где компилятор позаботится об этом автоматически.
Чтобы не спутать себя и других программистов с этой редко используемой функцией, вы всегда можете сделать внутренние классы статичными.
В случае, если требуется ссылка на внешний класс, вы можете передать ее явно в конструкторе.
Вы действительно можете сделать это, но вы должны объявить ClickEvent
какstatic
внутри Button
и тогда у вас не должно возникнуть проблем с использованием синтаксиса:
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) {
}
}
Ваш код скомпилируется, если бы вы набрали
buttonClick(new Button().ClickEvent(button));
вместо
buttonClick(new Button.ClickEvent(button));
поскольку конструктор - это метод, и когда вы вызываете метод в Java, вы должны передать список аргументов, даже если он пуст.