2009-03-11 6 views
41

ich nicht vorstellen, dass ich radikal neue Syntax in Java mehr in diesem Stadium auftreten würde, aber siehe da, ich habe gerade begegnet etwas:merkwürdige Syntax für eine innere Klasse instanziieren

Den genauen Zusammenhang und was die Code sollte tun, ist ziemlich irrelevant - es ist da, nur um eine Art von Kontext zu geben.

Ich versuche, diese Art von Linie zu synthetisch ein Ereignis in der IT-Mühle Toolkit zu erstellen, so schrieb ich:

buttonClick(new Button.ClickEvent(button)); 

Aber Eclipse gibt mir die folgende Fehlermeldung:

Auf eine umschließende Instanz des Typs Schaltfläche kann nicht zugegriffen werden. Muss die Zuordnung mit einer umschließenden Instanz vom Typ Button qualifizieren (z. B. x.new A(), wobei x eine Instanz von Button ist).

Wenn ich die Zeile oben wie folgt umschreiben, sie beschweren sich nicht mehr:

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

Also, meine Frage ist: Was bedeutet diese Syntax bedeutet, genau, und warum nicht der Fall ist die erste Schnipselarbeit? Worüber beschwert sich Java und was macht es in der zweiten Version?

Hintergrundinformationen: Sowohl Button als auch Button.ClickEvent sind nicht abstrakte öffentliche Klassen.

+0

Interessante Frage, aber der Titel vielleicht anspruchsvoller sein könnte. – mafu

+0

Sie können diese vorschlagen. Ich wusste damals nicht, wie ich die Syntax nennen sollte, also blieb der Titel leider seltsam. –

Antwort

69

Innere Klassen (wie Button.ClickEvent) müssen einen Verweis auf eine Instanz der äußeren Klasse (Button).

Diese Syntax erstellt eine neue Instanz von Button.ClickEvent, deren äußere Klassenreferenz auf den Wert button gesetzt ist.

Hier ist ein Beispiel - das Fehlen von Verkapselung usw. ignorieren, es ist nur für die Zwecke der 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(); 
    } 
} 

Siehe section 8.1.3 of the spec für mehr über innere Klassen und Instanzen umschließt.

+7

Wenn die innere Klasse den Verweis auf die äußere Klasse nicht benötigt, können Sie diese Anforderung eliminieren, indem Sie das statische Schlüsselwort in der inneren Klassendeklaration verwenden - statische Klasse Inner im obigen Beispiel - obwohl Sie dann natürlich keinen Zugriff hätten zum Outer.name Eigentum ...! –

+0

@Jon gibt es einen spezifischen technischen Namen für dieses Idiom der Erstellung einer nicht-statischen inneren Klasseninstanz? – Inquisitive

+0

@Inquisitive: Es ist nur eine Instanz einer inneren Klasse. –

9

Button.ClickEvent ist eine nicht statische innere Klasse, daher kann eine Instanz dieser Klasse nur in einer Instanz von Button enthalten sein.

In Ihrem zweiten Codebeispiel Sie eine Instanz von Button-und Sie erstellen eine Instanz von ClickEvent in dieser Button-Instanz eingeschlossen ...

8

Eine nicht statische innere Klasse in Java enthält einen versteckten Verweis, der auf eine Instanz der äußeren Klasse verweist, in der sie deklariert ist. Die Fehlermeldung, die Sie ursprünglich erhalten haben, sagt Ihnen, dass Sie keine neue Instanz des inneren erstellen können class ohne auch eine Instanz der äußeren Klasse anzugeben, an die sie angehängt werden soll.

Vielleicht ist der Grund, warum Sie diese Syntax vorher nicht gesehen haben, dass innere Klassen oft in einer Methode der äußeren Klasse zugeordnet werden, wo der Compiler das automatisch erledigt.

3

Um zu vermeiden, dass Sie und andere Programmierer mit dieser selten verwendeten Funktion verwechselt werden, können Sie innere Klassen immer statisch machen.

Falls ein Verweis auf die äußere Klasse benötigt wird, können Sie diese explizit im Konstruktor übergeben.

+0

Oder machen Sie innere Klassen privat und bleiben Sie dabei implizit. –

-1

Ihr Code würde kompilieren, mussten Sie

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

statt

buttonClick(new Button.ClickEvent(button));

als ein Konstruktor ist eine Methode, und wenn Sie eine Methode in Java aufrufen getippt müssen Sie die Liste der geben Argumente, auch wenn es leer ist.

+0

Eigentlich nein, das funktioniert nicht, weil es versucht, die Methode ClickEvent (Button) damit aufzurufen, die es nicht findet ... –

+0

new Button(). Neu ClickEvent (Button)? –

1

Sie können tatsächlich, dass tun, aber Sie haben ClickEvent als static innerhalb Button, zu erklären, und dann haben Sie sollten kein Problem Sie mit Sintax:

buttonClick(new Button.ClickEvent(button)); 

Grundsätzlich static macht die Klasse ClickEvent gehören direkt zu der Klasse Button anstelle einer bestimmten Instanz (dh new Button()) von Button.


Nach @ Jon Skeet Beispiel:

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