Java: Mehrere Klassendeklarationen in einer Datei

238

In Java können Sie mehrere Klassen der obersten Ebene in einer einzigen Datei definieren, vorausgesetzt, dass höchstens eine davon öffentlich ist (siehe JLS §7.6 ). Siehe unten zum Beispiel.

  1. Gibt es einen ordentlichen Namen für diese Technik (analog inner, nested, anonymous)?

  2. Das JLS sagt, dass das System möglicherweise die Einschränkung erzwingt, dass diese sekundären Klassen nicht sein können referred to by code in other compilation units of the package, z. B. können sie nicht als paketprivat behandelt werden. Ändert sich das wirklich zwischen Java-Implementierungen?

zB PublicClass.java:

package com.example.multiple;

public class PublicClass {
    PrivateImpl impl = new PrivateImpl();
}

class PrivateImpl {
    int implementationData;
}
Michael Brewer-Davis
quelle
11
+1 Gute Fragen. Ich habe nie wirklich darüber nachgedacht, da dies fast nie notwendig ist.
Michael Myers
12
Beachten Sie, dass dies ein Überbleibsel ist. Es wäre niemals möglich gewesen, wenn Java von Anfang an verschachtelte Klassen gehabt hätte.
Kevin Bourrillion

Antworten:

120

Mein vorgeschlagener Name für diese Technik (einschließlich mehrerer Klassen der obersten Ebene in einer einzigen Quelldatei) wäre "Chaos". Im Ernst, ich denke nicht, dass es eine gute Idee ist - ich würde stattdessen in dieser Situation einen verschachtelten Typ verwenden. Dann ist es immer noch leicht vorherzusagen, in welcher Quelldatei sie sich befindet. Ich glaube jedoch nicht, dass es einen offiziellen Begriff für diesen Ansatz gibt.

Ob sich dies tatsächlich zwischen den Implementierungen ändert - ich bezweifle es sehr, aber wenn Sie es überhaupt vermeiden, müssen Sie sich nie darum kümmern :)

Jon Skeet
quelle
71
Ich bin nicht der Downvoter, aber die Tatsache, dass diese Antwort als "normativ" bezeichnet werden könnte (dh "Sie sollten" anstelle von "tatsächlich ... jedoch ..."), ist der wahrscheinlichste Grund dafür bekomme eine Abwertung, denke ich. Es beantwortet eigentlich keine der Fragen. Als würde man eine irrelevante Ausnahme auslösen, anstatt etwas zurückzugeben, oder eine Ausnahme auslösen, die Informationen über tatsächliche Fakten anstelle von Meinungen enthält.
n611x007
6
Ich fand, was meiner Meinung nach eine kleine Ausnahme von @ JonSkeets Vorschlag ist, einen verschachtelten Typ zu verwenden (dem ich sonst zustimmen würde): Wenn die Hauptklasse generisch ist und der Typparameter die zweite Klasse ist, kann die zweite Klasse dies nicht verschachtelt sein. Und wenn die beiden Klassen eng miteinander verbunden sind (wie PublicClass und PrivateImpl in der Frage), ist es meiner Meinung nach eine gute Idee, PrivateImpl als Klasse der obersten Ebene in dieselbe Datei aufzunehmen.
jfritz42
6
@BoomerRogers: Nein, dies ist definitiv nicht die " Kernbasis der komponentenbasierten Programmierung". Wenn Sie für eine Komponente programmieren, warum interessiert es Sie dann, wie der Quellcode organisiert ist? (Ich persönlich bevor Abhängigkeit Injektion anstatt die Service Locator Muster, aber das ist eine andere Sache.) Separate API und Quellcode - Organisation in Ihrem Kopf - sie sind sehr verschiedene Dinge.
Jon Skeet
1
@ JonSkeet Lassen Sie mich umformulieren: Ihre "Antwort" ist eine persönliche irrelevante Meinung. (dh Antworten wie "Chaos" und "Ich bezweifle es" haben wenig Wert.) Ihr Beitrag beantwortet also keine der beiden gestellten Fragen. Überprüfen Sie die Antwort von Polygenschmierstoffen, und Sie werden sehen, dass er es schafft, beide zu beantworten.
bvdb
1
@bvdb: (Und es gibt viele Dinge, die schlechte Praxis sind, aber von der Spezifikation erlaubt. Ich würde die Leute dringend bitten, nicht public int[] foo(int x)[] { return new int[5][5]; }so gut zu schreiben , obwohl das gültig ist.)
Jon Skeet
130

javac verbietet dies nicht aktiv, hat jedoch eine Einschränkung, die so ziemlich bedeutet, dass Sie niemals auf eine Klasse der obersten Ebene aus einer anderen Datei verweisen möchten, es sei denn, sie hat denselben Namen wie die Datei, in der sie sich befindet.

Angenommen, Sie haben zwei Dateien, Foo.java und Bar.java.

Foo.java enthält:

  • öffentliche Klasse Foo

Bar.java enthält:

  • öffentliche Klasse Bar
  • Klasse Baz

Angenommen, alle Klassen befinden sich im selben Paket (und die Dateien befinden sich im selben Verzeichnis).

Was passiert, wenn Foo.java auf Baz, aber nicht auf Bar verweist und wir versuchen, Foo.java zu kompilieren? Die Kompilierung schlägt mit einem Fehler wie dem folgenden fehl:

Foo.java:2: cannot find symbol
symbol  : class Baz
location: class Foo
  private Baz baz;
          ^
1 error

Dies ist sinnvoll, wenn Sie darüber nachdenken. Wenn Foo.java auf Baz verweist, aber keine Baz.java (oder Baz.class) vorhanden ist, wie kann javac wissen, in welcher Quelldatei gesucht werden soll?

Wenn Sie stattdessen javac anweisen, Foo.java und Bar.java gleichzeitig zu kompilieren, oder wenn Sie zuvor Bar.java kompiliert haben (die Baz.class dort belassen, wo javac sie finden kann), verschwindet dieser Fehler. Dadurch fühlt sich Ihr Erstellungsprozess jedoch sehr unzuverlässig und schuppig an.

Weil die eigentliche Einschränkung eher so lautet: "Verweisen Sie nicht auf eine Klasse der obersten Ebene aus einer anderen Datei, es sei denn, sie hat denselben Namen wie die Datei, in der sie sich befindet, oder Sie beziehen sich auch auf eine Klasse, die sich in derselben Datei befindet, die benannt ist Das Gleiche wie die Datei "ist schwer zu befolgen. Die Leute gehen normalerweise mit der viel einfacheren (wenn auch strengeren) Konvention vor, nur eine Klasse der obersten Ebene in jede Datei einzufügen. Dies ist auch besser, wenn Sie jemals Ihre Meinung ändern, ob eine Klasse öffentlich sein soll oder nicht.

Manchmal gibt es wirklich einen guten Grund, warum jeder etwas auf eine bestimmte Weise tut.

Laurence Gonsalves
quelle
Tut Maven etwas, um die Kompilierung zuverlässig zu machen?
Aleksandr Dubinsky
23

Ich glaube, Sie nennen einfach, PrivateImplwas es ist: a non-public top-level class. Sie können auch deklarieren non-public top-level interfaces.

zB anderswo auf SO: Nicht öffentliche Klasse der obersten Ebene gegen statisch verschachtelte Klasse

In Bezug auf Verhaltensänderungen zwischen Versionen gab es diese Diskussion über etwas, das in 1.2.2 "perfekt funktioniert" hat. Die Arbeit in Version 1.4 im Sun-Forum wurde jedoch eingestellt: Java Compiler - Nicht öffentliche Klassen der obersten Ebene in einer Datei können nicht deklariert werden .

Polygenschmierstoffe
quelle
1
Mein einziges Problem dabei ist, dass Sie non-public top level classdie einzige Klasse in einer Datei sein können, sodass die Vielzahl nicht berücksichtigt wird.
Michael Brewer-Davis
Ich verstehe die Besorgnis, aber wie Sie sehen, ist dies eine Terminologie, die andere historisch verwendet haben. Wenn ich meine eigene Amtszeit zusammenstellen muss, werde ich sie wahrscheinlich nennen secondary top level types.
Polygenelubricants
7

Sie können so viele Klassen haben, wie Sie möchten

public class Fun {
    Fun() {
        System.out.println("Fun constructor");
    }
    void fun() {
        System.out.println("Fun mathod");
    }
    public static void main(String[] args) {
        Fun fu = new Fun();
        fu.fun();
        Fen fe = new Fen();
        fe.fen();
        Fin fi = new Fin();
        fi.fin();
        Fon fo = new Fon();
        fo.fon();
        Fan fa = new Fan();
        fa.fan();
        fa.run();
    }
}

class Fen {
    Fen() {
        System.out.println("fen construuctor");

    }
    void fen() {
        System.out.println("Fen method");
    }
}

class Fin {
    void fin() {
        System.out.println("Fin method");
    }
}

class Fon {
    void fon() {
        System.out.println("Fon method");
    } 
}

class Fan {
    void fan() {
        System.out.println("Fan method");
    }
    public void run() {
        System.out.println("run");
    }
}
Denis
quelle
1
@Nenotlep Wenn Sie eine "Formatierung verbessern", achten Sie bitte auch darauf, dass der Code selbst nicht beeinträchtigt wird, z. B. durch Entfernen von Backslashes.
Tom
3
Das beantwortet die Frage nicht.
1.
4

1. Gibt es einen ordentlichen Namen für diese Technik (analog zu inner, verschachtelt, anonym)?

Demo mit mehreren Dateien und mehreren Dateien.

2. Das JLS sagt, dass das System möglicherweise die Einschränkung erzwingt, dass diese sekundären Klassen in anderen Kompilierungseinheiten des Pakets nicht durch Code referenziert werden können, z. B. können sie nicht als paketprivat behandelt werden. Ändert sich das wirklich zwischen Java-Implementierungen?

Mir sind keine bekannt, die diese Einschränkung nicht haben. Bei allen dateibasierten Compilern können Sie nicht auf Quellcodeklassen in Dateien verweisen, die nicht den gleichen Namen wie der Klassenname haben. (Wenn Sie eine Datei mit mehreren Klassen kompilieren und die Klassen in den Klassenpfad einfügen, werden sie von jedem Compiler gefunden.)

Pete Kirkham
quelle
1

Laut Effective Java 2nd Edition (Punkt 13):

"Wenn eine paketprivate Klasse (oder Schnittstelle) der obersten Ebene nur von einer Klasse verwendet wird, sollten Sie die Klasse der obersten Ebene zu einer privat verschachtelten Klasse der einzigen Klasse machen, die sie verwendet (Element 22). Dies verringert die Zugänglichkeit für alle die Klassen in seinem Paket auf die eine Klasse, die es verwendet. Es ist jedoch weitaus wichtiger, die Zugänglichkeit einer unentgeltlich öffentlichen Klasse als einer paketprivaten Klasse der obersten Ebene zu verringern: ... "

Die verschachtelte Klasse kann statisch oder nicht statisch sein, je nachdem, ob die Mitgliedsklasse Zugriff auf die einschließende Instanz benötigt (Element 22).

rezzy
quelle
Das OP fragt nicht nach verschachtelten Klassen.
charmoniumQ
0

Ja, Sie können mit öffentlichen statischen Mitgliedern in einer äußeren öffentlichen Klasse wie folgt:

public class Foo {

    public static class FooChild extends Z {
        String foo;
    }

    public static class ZeeChild extends Z {

    }

}

und eine andere Datei, die auf das oben Genannte verweist:

public class Bar {

    public static void main(String[] args){

        Foo.FooChild f = new Foo.FooChild();
        System.out.println(f);

    }
}

Legen Sie sie in den gleichen Ordner. Kompilieren mit:

javac folder/*.java

und laufen mit:

 java -cp folder Bar
Alexander Mills
quelle
Dieses Beispiel beantwortet die Frage nicht. Sie geben ein Beispiel für verschachtelte statische Klassen, das nicht mit der Definition von zwei Klassen der obersten Ebene in derselben Datei identisch ist.
Pedro García Medina
0

Nur zu Ihrer Information, wenn Sie Java 11+ verwenden, gibt es eine Ausnahme von dieser Regel: Wenn Sie Ihre Java-Datei direkt ausführen ( ohne Kompilierung ). In diesem Modus gibt es keine Einschränkung für eine einzelne öffentliche Klasse pro Datei. Die Klasse mit der mainMethode muss jedoch die erste in der Datei sein.

ZhekaKozlov
quelle
-5

Nein, das kannst du nicht. Aber in Scala ist es sehr gut möglich:

class Foo {val bar = "a"}
class Bar {val foo = "b"}
Michal Štein
quelle