In Java habe ich gerade herausgefunden, dass der folgende Code legal ist:
KnockKnockServer newServer = new KnockKnockServer();
KnockKnockServer.receiver receive = newServer.new receiver(clientSocket);
Zu Ihrer Information, der Empfänger ist nur eine Hilfsklasse mit der folgenden Signatur:
public class receiver extends Thread { /* code_inside */ }
Ich habe die XYZ.new
Notation noch nie gesehen . Wie funktioniert das? Gibt es eine Möglichkeit, dies konventioneller zu codieren?
new
ein Operator in vielen Sprachen ist. (Ich dachte, Sie könnten auchnew
in C ++ überladen ?) Javas innere Klasse ist für mich allerdings etwas seltsam.Antworten:
Auf diese Weise können Sie eine nicht statische innere Klasse von außerhalb des enthaltenen Klassenkörpers instanziieren, wie in den Oracle-Dokumenten beschrieben .
Jede innere Klasseninstanz ist einer Instanz ihrer enthaltenden Klasse zugeordnet. Wenn Sie
new
eine innere Klasse von innerhalb seiner Klasse enthält , verwendet es diethis
Instanz des Behälters durch Standard:Wenn Sie jedoch eine Instanz von Bar außerhalb von Foo erstellen oder eine neue Instanz einer anderen enthaltenen Instanz zuordnen möchten,
this
müssen Sie die Präfixnotation verwenden.quelle
f.new
.public
Zugriffsebene aufKnockKnockServer.receiver
gemacht wurden ,private
dann wäre es auf diese Weise zu instantiate unmöglich sein, nicht wahr? Um den Kommentar von @EricJablow zu erweitern, sollten innere Klassen im Allgemeinen immer standardmäßig eineprivate
Zugriffsebene verwenden.receiver
von außen überhaupt auf die Klasse zu verweisen . Wenn ich es entwerfen würde, hätte ich wahrscheinlich die Klasse public, aber ihren Konstruktor geschützt oder package-private und eine MethodeKnockKnockServer
zum Erstellen von Empfängerinstanzen.x.new
.Schauen Sie sich dieses Beispiel an:
Mit javap können wir Anweisungen anzeigen, die für diesen Code generiert wurden
Hauptmethode:
Konstruktor der inneren Klasse:
Alles ist einfach - beim Aufrufen des TestInner-Konstruktors übergibt Java die Testinstanz als erstes Argument main: 12 . Wenn Sie sich diesen TestInner nicht ansehen, sollte er einen Konstruktor ohne Argumente haben. TestInner wiederum speichert nur den Verweis auf das übergeordnete Objekt Test $ TestInner: 2 . Wenn Sie den Konstruktor der inneren Klasse über eine Instanzmethode aufrufen, wird der Verweis auf das übergeordnete Objekt automatisch übergeben, sodass Sie ihn nicht angeben müssen. Eigentlich geht es jedes Mal vorbei, aber wenn es von außerhalb aufgerufen wird, sollte es explizit übergeben werden.
t.new TestInner();
- ist nur eine Möglichkeit, das erste versteckte Argument für den TestInner-Konstruktor anzugeben, nicht einen Typmethod () ist gleich:
TestInner ist gleich:
quelle
Wenn Java in Version 1.1 der Sprache innere Klassen hinzugefügt wurden, wurden sie ursprünglich als Umwandlung in 1.0-kompatiblen Code definiert. Wenn Sie sich ein Beispiel für diese Transformation ansehen, wird es meiner Meinung nach viel klarer machen, wie eine innere Klasse tatsächlich funktioniert.
Betrachten Sie den Code aus Ian Roberts 'Antwort:
Bei der Transformation in 1.0-kompatiblen Code
Bar
würde diese innere Klasse ungefähr so aussehen:Dem inneren Klassennamen wird der äußere Klassenname vorangestellt, um ihn eindeutig zu machen. Ein verstecktes privates
this$0
Mitglied wird hinzugefügt, das eine Kopie des äußeren enthältthis
. Und ein versteckter Konstruktor wird erstellt, um dieses Mitglied zu initialisieren.Und wenn Sie sich die
createBar
Methode ansehen , würde sie sich in etwa so verwandeln:Mal sehen, was passiert, wenn Sie den folgenden Code ausführen.
Zuerst instanziieren wir eine Instanz von
Foo
und initialisieren dasval
Mitglied auf 5 (dhf.val = 5
).Als nächstes rufen wir auf
f.createBar()
, das eine Instanz von instanziiertFoo$Bar
und dasthis$0
Mitglied auf den Wert vonthis
übergeben voncreateBar
(dhb.this$0 = f
) initialisiert .Schließlich rufen wir auf,
b.printVal()
welche Versuche zu druckenb.this$0.val
sind,f.val
welche 5 sind.Das war eine regelmäßige Instanziierung einer inneren Klasse. Schauen wir uns an, was passiert, wenn
Bar
von außen instanziiert wirdFoo
.Wenn Sie unsere 1.0-Transformation erneut anwenden, wird diese zweite Zeile ungefähr so aussehen:
Dies ist fast identisch mit dem
f.createBar()
Anruf. Wieder instanziieren wir eine Instanz vonFoo$Bar
und initialisieren dasthis$0
Mitglied auf f. Also nochmal ,b.this$0 = f
.Und wieder, wenn Sie anrufen
b.printVal()
, drucken Sieb.thi$0.val
,f.val
was 5 ist.Das Wichtigste ist, dass die innere Klasse ein verstecktes Mitglied hat, das eine Kopie
this
der äußeren Klasse enthält. Wenn Sie eine innere Klasse innerhalb der äußeren Klasse instanziieren, wird sie implizit mit dem aktuellen Wert von initialisiertthis
. Wenn Sie die innere Klasse von außerhalb der äußeren Klasse instanziieren, geben Sie über das Präfix desnew
Schlüsselworts explizit an, welche Instanz der äußeren Klasse verwendet werden soll .quelle
Stellen Sie sich
new receiver
ein einzelnes Zeichen vor. Ein bisschen wie ein Funktionsname mit einem Leerzeichen.Natürlich hat die Klasse
KnockKnockServer
nicht buchstäblich eine Funktion namensnew receiver
, aber ich vermute, die Syntax soll dies vorschlagen. Es soll so aussehen, als würden Sie eine Funktion aufrufen, die eine neue Instanz derKnockKnockServer.receiver
Verwendung einer bestimmten InstanzKnockKnockServer
für alle Zugriffe auf die einschließende Klasse erstellt.quelle
new receiver
als ein Zeichen vorzustellen! vielen Dank!Beschattung
Wenn eine Deklaration eines Typs (z. B. eine Mitgliedsvariable oder ein Parametername) in einem bestimmten Bereich (z. B. einer inneren Klasse oder einer Methodendefinition) denselben Namen wie eine andere Deklaration im umschließenden Bereich hat, wird die Deklaration durch die Deklaration beschattet des umschließenden Umfangs. Sie können eine schattierte Deklaration nicht nur mit ihrem Namen bezeichnen. Das folgende Beispiel, ShadowTest, zeigt dies:
Das Folgende ist die Ausgabe dieses Beispiels:
In diesem Beispiel werden drei Variablen mit dem Namen x definiert: Die Elementvariable der Klasse ShadowTest, die Elementvariable der inneren Klasse FirstLevel und der Parameter in der Methode methodInFirstLevel. Die als Parameter der Methode methodInFirstLevel definierte Variable x schattiert die Variable der inneren Klasse FirstLevel. Wenn Sie die Variable x in der Methode methodInFirstLevel verwenden, verweist sie folglich auf den Methodenparameter. Verwenden Sie das Schlüsselwort this, um den umschließenden Bereich darzustellen, um auf die Elementvariable der inneren Klasse FirstLevel zu verweisen:
Verweisen Sie auf Elementvariablen, die größere Bereiche nach dem Klassennamen einschließen, zu dem sie gehören. Die folgende Anweisung greift beispielsweise über die Methode methodInFirstLevel auf die Mitgliedsvariable der Klasse ShadowTest zu:
Siehe die Dokumente
quelle