简体   繁体   English

用于实例化内部类的奇怪语法

[英]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: 我没想到在这个阶段我会在Java中遇到全新的语法,但是,我发现,我刚刚遇到了一些问题:

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: 我正在尝试综合在IT Mill Toolkit中创建一个事件,所以我写了这样一行:

buttonClick(new Button.ClickEvent(button));

But, Eclipse gives me the following error message: 但是,Eclipse给出了以下错误消息:

No enclosing instance of type Button is accessible. 不能访问类型为Button的封闭实例。 Must qualify the allocation with an enclosing instance of type Button (egxnew A() where x is an instance of Button). 必须使用Button类型的封闭实例限定分配(egxnew A(),其中x是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? 什么是Java抱怨,以及它在第二个版本中做了什么?

Background info: Both Button and Button.ClickEvent are non-abstract public classes. 背景信息: ButtonButton.ClickEvent都是非抽象的公共类。

Inner classes (like Button.ClickEvent ) need a reference to an instance of the outer class ( Button ). 内部类(如Button.ClickEvent )需要引用外部类的实例( Button )。

That syntax creates a new instance of Button.ClickEvent with its outer class reference set to the value of button . 该语法创建一个Button.ClickEvent的新实例,其外部类引用设置为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. 有关内部类和封闭实例的更多信息,请参阅规范的8.1.3节

Button.ClickEvent is a non-static inner class so an instance of this class can only exist enclosed in a instance of Button. Button.ClickEvent是一个非静态内部类,因此该类的实例只能包含在Button的实例中。

In your second code example you have an instance of Button and you create an instance of ClickEvent enclosed in this Button instance... 在第二个代码示例中,您有一个Button实例,并在此Button实例中创建一个ClickEvent实例...

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. Java中的非静态内部类包含一个隐藏引用,该引用指向它声明的外部类的实例。因此,您最初获得的错误消息告诉您,如果不指定内部类的新实例,则无法指定要附加到的外部类的实例。

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. 也许你之前没有看到过这种语法的原因是内部类通常在外部类的方法中分配,编译器会自动处理它。

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. 如果需要引用外部类,您可以在构造函数中显式传递它。

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: 你实际上可以做到这一点 ,但你必须在Button声明ClickEventstatic ,然后使用ClickEvent你应该没有任何问题:

buttonClick(new Button.ClickEvent(button));

Basically static makes the class ClickEvent belong directly to the class Button instead of a specific instance(ie new Button() ) of Button . 基本上static使得类ClickEvent直接属于类Button ,而不是一个具体的实例(即new Button()Button


Following @Jon Skeet example: 关注@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) {
    }
}

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. 作为构造函数是一种方法,当您在Java中调用方法时,您必须传递参数列表,即使它是空的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM