Java: dichiarazioni di più classi in un file


237

In Java, è possibile definire più classi di livello superiore in un singolo file, purché al massimo una di queste sia pubblica (vedere JLS §7.6 ). Vedi sotto per esempio.

  1. C'è un nome in ordine per questa tecnica (analogo a inner, nested, anonymous)?

  2. JLS afferma che il sistema potrebbe imporre la restrizione che queste classi secondarie non possono essere referred to by code in other compilation units of the package, ad esempio, non possono essere trattate come pacchetto-private. È davvero qualcosa che cambia tra le implementazioni Java?

ad es. PublicClass.java:

package com.example.multiple;

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

class PrivateImpl {
    int implementationData;
}

11
+1 Buone domande. Non ci ho mai pensato molto, dato che non è quasi mai necessario farlo.
Michael Myers

12
si noti che questa è una caratteristica rudimentale; non sarebbe mai stato possibile se java avesse avuto classi nidificate sin dall'inizio.
Kevin Bourrillion,

Risposte:


120

Il mio nome suggerito per questa tecnica (comprese più classi di livello superiore in un singolo file sorgente) sarebbe "pasticcio". Scherzi a parte, non credo sia una buona idea, in questa situazione utilizzerei un tipo nidificato. Quindi è ancora facile prevedere in quale file sorgente si trova. Non credo che ci sia un termine ufficiale per questo approccio.

Per quanto riguarda se questo cambia effettivamente tra le implementazioni - ne dubito fortemente, ma se eviti di farlo in primo luogo, non dovrai mai preoccuparti :)


71
Non sono il downvoter, ma il fatto che questa risposta sia qualcosa che potrebbe essere definita "normativa" (cioè "dovresti" invece di "in effetti ... comunque ...") è la ragione più probabile per cui ottenere un voto negativo, penso. In realtà non risponde a nessuna delle domande. Come sollevare un'eccezione irrilevante invece di restituire qualcosa / sollevare un'eccezione che contiene informazioni su fatti reali anziché opinioni.
n611x007,

6
Ho trovato quella che penso sia un'eccezione minore al suggerimento di @JonSkeet di usare un tipo nidificato (che altrimenti sarei d'accordo): se la classe principale è generica e il parametro type è la seconda classe, la seconda classe non può essere annidato. E se le due classi sono strettamente accoppiate (come PublicClass e PrivateImpl nella domanda), penso che sia una buona idea mettere PrivateImpl come classe di primo livello nello stesso file.
jfritz42,

6
@BoomerRogers: No, questo è sicuramente non è la "base nucleo di programmazione basato componente". Se stai programmando contro un componente, perché dovresti preoccuparti di come è organizzato il codice sorgente? (Personalmente preferisco l' iniezione di dipendenza piuttosto che il modello di localizzazione del servizio, ma è una questione diversa.) Organizzazione separata di API e codice sorgente nella tua mente - sono cose molto diverse.
Jon Skeet,

1
@JonSkeet Lasciami riformulare: la tua "risposta" è un'opinione personale irrilevante. (vale a dire risposte come "pasticcio" e "ne dubito" hanno poco valore.) Quindi, il tuo post non risponde a nessuna delle 2 domande poste. Controlla la risposta dei poligenelubrificanti e vedrai che riesce a rispondere ad entrambi.
bvdb

1
@bvdb: (E ci sono molte cose che sono cattive pratiche ma permesse dalle specifiche. Vorrei esortare le persone a non scriverepublic int[] foo(int x)[] { return new int[5][5]; } anche, anche se questo è valido.)
Jon Skeet

130

javac non lo proibisce attivamente, ma ha una limitazione che praticamente significa che non vorresti mai fare riferimento a una classe di livello superiore da un altro file a meno che non abbia lo stesso nome del file in cui si trova.

Supponiamo di avere due file, Foo.java e Bar.java.

Foo.java contiene:

  • classe pubblica Foo

Bar.java contiene:

  • Bar di classe pubblica
  • classe Baz

Diciamo anche che tutte le classi si trovano nello stesso pacchetto (e che i file si trovano nella stessa directory).

Cosa succede se Foo.java si riferisce a Baz ma non a Bar e proviamo a compilare Foo.java? La compilazione non riesce con un errore come questo:

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

Questo ha senso se ci pensi. Se Foo.java fa riferimento a Baz, ma non esiste Baz.java (o Baz.class), come può javac sapere in quale file sorgente cercare?

Se invece dici a javac di compilare Foo.java e Bar.java allo stesso tempo, o anche se avevi precedentemente compilato Bar.java (lasciando la classe Baz. dove javac può trovarlo), questo errore scompare. Questo rende il tuo processo di compilazione molto inaffidabile e traballante, tuttavia.

Perché la limitazione effettiva, che è più simile a "non si riferisce a una classe di livello superiore da un altro file a meno che non abbia lo stesso nome del file in cui si trova o si riferisca anche a una classe che si trova nello stesso file che è denominato la stessa cosa del file "è un po 'difficile da seguire, le persone di solito vanno con la convenzione molto più semplice (anche se più rigorosa) di mettere solo una classe di livello superiore in ciascun file. Questo è anche meglio se cambi idea sul fatto che una classe debba essere pubblica o meno.

A volte c'è davvero una buona ragione per cui tutti fanno qualcosa in un modo particolare.


Maven fa qualcosa per rendere affidabile la compilazione?
Aleksandr Dubinsky,

23

Credo che tu chiami semplicemente PrivateImplquello che è: a non-public top-level class. Puoi anche dichiarare non-public top-level interfacesanche.

ad es. altrove su SO: classe di livello superiore non pubblica vs classe nidificata statica

Per quanto riguarda i cambiamenti nel comportamento tra le versioni, c'è stata questa discussione su qualcosa che "ha funzionato perfettamente" in 1.2.2. ma ha smesso di funzionare in 1.4 nel forum di Sun: Java Compiler - impossibile dichiarare classi di livello superiore non pubbliche in un file .


1
Il mio unico problema con questo è che puoi avere una non-public top level classsola classe in un file, quindi non affronta la molteplicità.
Michael Brewer-Davis,

Capisco la preoccupazione, ma come puoi vedere questa è una terminologia che altri hanno storicamente usato. Se devo inventare il mio mandato, probabilmente lo chiamerò secondary top level types.
poligenelubrificanti

7

Puoi avere tutte le lezioni che desideri in questo modo

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

1
@Nenotlep Quando esegui un "miglioramento della formattazione", ti preghiamo di fare attenzione a non rovinare il codice stesso, come rimuovere le barre rovesciate.
Tom

3
Questo non risponde alla domanda.
ᴠɪɴᴄᴇɴᴛ

4

1. Esiste un nome ordinato per questa tecnica (analogo a interno, nidificato, anonimo)?

Demo multi-classe a file singolo.

2. Il JLS afferma che il sistema potrebbe imporre la restrizione che queste classi secondarie non possono essere indicate dal codice in altre unità di compilazione del pacchetto, ad esempio, non possono essere trattate come pacchetto-privato. È davvero qualcosa che cambia tra le implementazioni Java?

Non sono a conoscenza di nessuno che non abbia questa limitazione - tutti i compilatori basati su file non ti permetteranno di fare riferimento a classi di codice sorgente in file che non hanno lo stesso nome del nome della classe. (se si compila un file multi-classe e si inseriscono le classi nel percorso della classe, qualsiasi compilatore le troverà)


1

Secondo Effective Java 2nd edition (Item 13):

"Se una classe di livello superiore (o interfaccia) privata del pacchetto viene utilizzata da una sola classe, considerare di rendere la classe di livello superiore una classe nidificata privata della sola classe che la utilizza (elemento 22). Ciò riduce la sua accessibilità da tutti le classi nel suo pacchetto a quella che lo utilizza. Ma è molto più importante ridurre l'accessibilità di una classe pubblica gratuitamente rispetto a una classe di livello superiore pacchetto privato: ... "

La classe nidificata può essere statica o non statica in base al fatto che la classe membro debba accedere all'istanza che la racchiude (elemento 22).


L'OP non chiede informazioni sulle classi nidificate.
charmoniumQ

0

Sì, puoi, con membri statici pubblici su una classe pubblica esterna, in questo modo:

public class Foo {

    public static class FooChild extends Z {
        String foo;
    }

    public static class ZeeChild extends Z {

    }

}

e un altro file che fa riferimento a quanto sopra:

public class Bar {

    public static void main(String[] args){

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

    }
}

metterli nella stessa cartella. Compilare con:

javac folder/*.java

e corri con:

 java -cp folder Bar

Quell'esempio non risponde alla domanda. Stai fornendo un esempio di classi statiche nidificate, che non è lo stesso di avere due classi di livello superiore definite nello stesso file.
Pedro García Medina,

0

Cordiali saluti, se si utilizza Java 11+, esiste un'eccezione a questa regola: se si esegue direttamente il file java ( senza compilazione ). In questa modalità, non vi sono restrizioni su una singola classe pubblica per file. Tuttavia, la classe con il mainmetodo deve essere la prima nel file.


-5

No. Non puoi. Ma è molto possibile in Scala:

class Foo {val bar = "a"}
class Bar {val foo = "b"}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.