web-dev-qa-db-de.com

Java: Mehrere Klassendeklarationen in einer Datei

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

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

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

z. B. PublicClass.Java:

package com.example.multiple;

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

class PrivateImpl {
    int implementationData;
}
218

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

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

116
Jon Skeet

javac verbietet dies nicht aktiv, aber es gibt eine Einschränkung, die beinahe bedeutet, dass Sie niemals auf eine Top-Level-Klasse 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

Sagen wir auch, dass sich alle Klassen im selben Paket befinden (und die Dateien im selben Verzeichnis liegen).

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

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

Das macht Sinn, wenn Sie darüber nachdenken. Wenn sich Foo.Java auf Baz bezieht, aber kein Baz.Java (oder Baz.class) vorhanden ist, woher kann Javac wissen, in welcher Quelldatei es sich befindet?

Wenn Sie javac stattdessen sagen, dass er gleichzeitig Foo.Java und Bar.Java kompilieren soll, oder wenn Sie zuvor Bar.Java kompiliert haben (die Baz.class-Klasse, in der javac sie finden kann), wird dieser Fehler behoben. Dadurch fühlt sich Ihr Build-Prozess jedoch sehr unzuverlässig und flockig an.

Die eigentliche Einschränkung, die eher wie "eine Referenz auf eine oberste Klasse aus einer anderen Datei" ist, hat nur den gleichen Namen wie die Datei, in der sie sich befindet, oder Sie beziehen sich auch auf eine Klasse, die sich in derselben Datei befindet Dasselbe wie in der Datei "ist irgendwie schwer zu folgen, die Leute gehen in der Regel mit der viel einfacheren (wenn auch strengeren) Konvention davon aus, nur eine Klasse der obersten Ebene in jede Datei zu setzen. Dies ist auch besser, wenn Sie Ihre Meinung darüber ändern, ob eine Klasse öffentlich sein soll oder nicht.

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

122

Ich glaube, Sie nennen einfach PrivateImpl was es ist: ein non-public top-level class. Sie können auch non-public top-level interfaces angeben.

z. B. an anderer Stelle in SO: Nicht-öffentliche Klasse der Klasse [] vs. statisch verschachtelte Klasse

Was das Verhalten zwischen den Versionen angeht, so wurde in 1.2.2 darüber diskutiert, dass etwas "perfekt funktioniert" hat. funktionierte aber in 1.4 im Sun-Forum nicht mehr: Java Compiler - Es kann keine nicht öffentlichen Top-Level-Klassen in einer Datei deklariert werden .

22

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");
    }
}
6
Denis

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

Einzelklassen-Demo für mehrere Klassen.

2.Das JLS sagt, dass das System die Einschränkung durchsetzen kann, dass diese sekundären Klassen nicht durch Code in anderen Kompilierungseinheiten des Pakets 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 - alle dateibasierten Compiler erlauben es Ihnen nicht, auf Quellcode-Klassen in Dateien zu verweisen, deren Name nicht mit dem Klassennamen identisch ist. (Wenn Sie eine Datei mit mehreren Klassen kompilieren und die Klassen in den Klassenpfad einfügen, werden sie von jedem Compiler gefunden.)

4
Pete Kirkham

Gemäß Effective Java 2nd Edition (Punkt 13):

Msgstr "" "Wenn eine Klasse (oder Schnittstelle) für private Klassen der obersten Ebene (oder Schnittstelle) nur von Einer Klasse verwendet wird, sollten Sie die oberste Klasse als private verschachtelte Klasse Der einzigen Klasse definieren, die sie verwendet (Element 22). Dies reduziert seine -Zugänglichkeit von allen Klassen in seinem Paket auf die eine Klasse .__, die es verwendet. Es ist jedoch viel wichtiger, die Zugänglichkeit Levelklasse: ... "

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

1
rezzy

Ja, das können Sie 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 Obige verweist:

public class Bar {

    public static void main(String[] args){

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

    }
}

lege sie in den gleichen Ordner. Kompilieren mit:

javac folder/*.Java

und laufen mit:

 Java -cp folder Bar
0
Alexander Mills