Perché un'interfaccia nidificata statica dovrebbe essere utilizzata in Java?


235

Ho appena trovato un'interfaccia nidificata statica nella nostra base di codice.

class Foo {
    public static interface Bar {
        /* snip */
    }
    /* snip */
}

Non l'ho mai visto prima. Lo sviluppatore originale è fuori portata. Pertanto devo chiedere SO:

Quali sono le semantiche dietro un'interfaccia statica? Cosa cambierebbe se rimuovessi static? Perché qualcuno dovrebbe farlo?


2
Questa non è un'interfaccia interna: è un'interfaccia nidificata . Inner ha un significato specifico in Java.
Marchese di Lorne,

Risposte:


293

La parola chiave statica nell'esempio sopra è ridondante (un'interfaccia nidificata è automaticamente "statica") e può essere rimossa senza alcun effetto sulla semantica; Consiglierei di essere rimosso. Lo stesso vale per "pubblico" sui metodi di interfaccia e "pubblico finale" sui campi di interfaccia: i modificatori sono ridondanti e aggiungono solo disordine al codice sorgente.

Ad ogni modo, lo sviluppatore sta semplicemente dichiarando un'interfaccia chiamata Foo.Bar. Non esiste alcuna ulteriore associazione con la classe che lo racchiude, ad eccezione del fatto che il codice che non può accedere a Foo non sarà in grado di accedere a Foo.Bar. (Dal codice sorgente - bytecode o reflection può accedere a Foo.Bar anche se Foo è un pacchetto privato!)

È accettabile creare un'interfaccia nidificata in questo modo se si prevede che venga utilizzata solo dalla classe esterna, in modo da non creare un nuovo nome di livello superiore. Per esempio:

public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});

1
Nella risposta di Jesse Glick, cosa significa: (Dal codice sorgente - bytecode o reflection possono accedere a Foo.Bar anche se Foo è privato del pacchetto!).
Vasu,

Kaillash, è possibile accedere ai metodi privati ​​tramite relection (nel pacchetto di riflessione) e accedendo direttamente al bytecode dei file .class generati.
gmoore,

2
Per "bytecode ... can access Foo.Bar" intendo che una classe compilata che fa riferimento a Foo.Bar può essere caricata ed eseguita anche se non può fare riferimento a Foo. Questo potrebbe accadere se la classe fosse stata compilata in un momento precedente quando Foo era pubblica, o se la classe fosse assemblata a mano o compilata da un linguaggio non Java, ecc. Il compilatore Java, tuttavia, controlla il modificatore di accesso sulla classe che lo racchiude anche quando il bytecode risultante non farebbe riferimento a quella classe che lo racchiude.
Jesse Glick,

@Jesse È possibile accedere a una classe statica privata in una classe privata di primo livello tramite la riflessione?
Pacerier

1
@Pacerier: no. Più precisamente, è possibile caricare la classe e ispezionarne i membri, ma non è possibile creare un'istanza o chiamare i metodi su di essa senza utilizzare setAccessible (true). In altre parole è visibile, ma non accessibile, tramite la riflessione. Ma se la classe statica nidificata è pubblica, è accessibile per impostazione predefinita tramite reflection anche se non è accessibile staticamente (durante la compilazione).
Jesse Glick

72

Alla domanda è stata data una risposta, ma un buon motivo per usare un'interfaccia nidificata è se la sua funzione è direttamente correlata alla classe in cui si trova. Un buon esempio di questo è a Listener. Se tu avessi una classe Fooe volessi che altre classi potessero ascoltare eventi su di essa, potresti dichiarare un'interfaccia chiamata FooListener, il che è ok, ma probabilmente sarebbe più chiaro dichiarare un'interfaccia nidificata e far implementare quelle altre classi Foo.Listener( una classe nidificata Foo.Eventnon è male insieme a questo).


11
Un esempio tipico è java.util.Map.Entry(che è un'interfaccia nidificata in un'altra interfaccia).
Paŭlo Ebermann,

4
So che questo è un vecchio argomento, ma preferirei che la classe esterna fosse nel suo pacchetto e che eventuali interfacce supplementari (es. Map.Entry) O classi fossero anch'esse all'interno di quel pacchetto. Lo dico perché mi piace mantenere le lezioni brevi e puntuali. Inoltre un lettore può vedere quali altre entità sono correlate a una classe osservando le classi nel pacchetto. Probabilmente avrei un pacchetto java.collections.mapper le mappe. Si tratta di OO e modularità. java.utilha troppo in esso. utilè come common- un odore IMO
David Kerr

1
@Shaggy: java.utilsicuramente ha troppo. Detto questo, non penso che scomporlo in pacchetti così raffinati come stai suggerendo sia l'ideale.
ColinD

1
@DavidKerr Supponiamo che tu chiami questa interfaccia java.util.MapEntryal di fuori del suo pacchetto. Primo riflesso: quali sono le interfacce relative a questa classe? Guardo in classe. A meno che javadoc non si colleghi a questa interfaccia, non ho idea della loro relazione. Inoltre le persone non guardano il pacchetto di importazione. Ognuno ha le proprie opinioni. Nessuno ha ragione, nessuno ha torto
Raymond Chenon,

@RaymondChenon parte di ciò che ho detto è stato quello di mettere Map e le sue classi associate, ad esempio Map.Entry in un pacchetto separato, ad esempio java.collections.map. In questo modo tutto il codice relativo alla mappa si trova in un'unica posizione (il pacchetto) e la classe Map di livello superiore non è gravata. Forse metterei le relative implementazioni (HashMap, ecc.) Nello stesso pacchetto.
David Kerr,

14

Le interfacce dei membri sono implicitamente statiche. Il modificatore statico nel tuo esempio può essere rimosso senza cambiare la semantica del codice. Vedi anche la specifica del linguaggio Java 8.5.1. Dichiarazioni sul tipo di membro statico


Questa non è una "interfaccia interna": è un'interfaccia nidificata. Inner ha un significato specifico in Java.
Marchese di Lorne,

@EJP, ho letto vari blog usando i termini in modo intercambiabile. Esiste un'interfaccia interna? In che modo differiscono dall'interfaccia nidificata?
Numero 945

@BreakingBenjamin, l'interfaccia nidificata significa statica. Esistono classe interna e classe nidificata, ma non interfaccia interna. Anche in questo caso, dovrebbe essere chiamato come interfaccia nidificata. Interno: non statico e nidificato è statico.
Sundar Rajan,

9

Un'interfaccia interna deve essere statica per poter accedere. L'interfaccia non è associata alle istanze della classe, ma alla classe stessa, quindi vi si accederà Foo.Bar, in questo modo:

public class Baz implements Foo.Bar {
   ...
}

Nella maggior parte dei modi, questo non è diverso da una classe interna statica.


35
Un'interfaccia nidificata è automaticamente statica, indipendentemente dal fatto che si scriva o meno la parola chiave.
Paŭlo Ebermann,

3
Abbiamo davvero bisogno di un modo per la comunità a votare per accettare una risposta diversa: stackoverflow.com/a/74400/632951
Pacerier


@ ClintonN.Dreisbach Puoi spiegare cosa si intende per The interface isn't associated with instances of the class, but with the class itselfpiù, non l'ho capito
Kasun Siyambalapitiya

6

La risposta di Jesse è vicina, ma penso che ci sia un codice migliore per dimostrare perché un'interfaccia interna può essere utile. Guarda il codice qui sotto prima di continuare a leggere. Riesci a capire perché l'interfaccia interna è utile? La risposta è che la classe DoSomethingAl già può essere istanziata con un'interfaccia interna che consente la costruzione di tipi personalizzati e ne migliora l'incapsulamento . qualsiasi classe che implementa A e C; non solo lo zoo di classe concreto. Naturalmente, questo può essere ottenuto anche se AC non è interno, ma immagina di concatenare nomi più lunghi (non solo A e C), e farlo per altre combinazioni (diciamo, A e B, C e B, ecc.) E tu facilmente guarda come le cose vanno fuori controllo. Per non parlare del fatto che le persone che recensiscono il tuo albero dei sorgenti saranno sopraffatte da interfacce significative solo in una classe. Quindi, per riassumere,

class ConcreteA implements A {
 :
}

class ConcreteB implements B {
 :
}

class ConcreteC implements C {
 :
}

class Zoo implements A, C {
 :
}

class DoSomethingAlready {
  interface AC extends A, C { }

  private final AC ac;

  DoSomethingAlready(AC ac) {
    this.ac = ac;
  }
}

2
Questo non ha senso. La classe Zoonon non implementa l'interfaccia AC, di conseguenza, i casi di Zoonon possono essere passati al costruttore DoSomethingAlready, che si aspetta AC. Il fatto che ACestende entrambi, Ae C, non implica che le classi implementino Ae implementino Cmagicamente AC.
Holger,


0

In genere vedo classi interne statiche. Le classi interne statiche non possono fare riferimento alle classi contenenti dove possono le classi non statiche. A meno che tu non stia incontrando alcune collisioni di pacchetti (esiste già un'interfaccia chiamata Bar nello stesso pacchetto di Foo), penso che lo farei diventare un suo file. Potrebbe anche essere una decisione progettuale far valere la connessione logica tra Foo e Bar. Forse l'autore voleva che Bar venisse usato solo con Foo (sebbene un'interfaccia interna statica non lo imponga, solo una connessione logica)


"Statico interiore" è una contraddizione in termini. Classi nidificate sono sia statico o interna.
Marchese di Lorne,

0

Se cambi la classe Foo nell'interfaccia Foo, anche la parola chiave "pubblica" nell'esempio sopra sarà ridondante perché

l'interfaccia definita all'interno di un'altra interfaccia sarà implicitamente pubblica statica.


Non ha risposto alla domanda
Raining,

0

Nel 1998, Philip Wadler ha suggerito una differenza tra interfacce statiche e interfacce non statiche.

Per quanto posso vedere, l'unica differenza nel rendere un'interfaccia non statica è che ora può includere classi interne non statiche; quindi la modifica non renderebbe non validi i programmi Java esistenti.

Ad esempio, ha proposto una soluzione al Problema di espressione , che è la discrepanza tra espressione come "quanto può esprimere la tua lingua" da un lato ed espressione come "i termini che stai cercando di rappresentare nella tua lingua" dall'altro .

Un esempio della differenza tra interfacce nidificate statiche e non statiche può essere visto nel suo codice di esempio :

// This code does NOT compile
class LangF<This extends LangF<This>> {
    interface Visitor<R> {
        public R forNum(int n);
    }

    interface Exp {
        // since Exp is non-static, it can refer to the type bound to This
        public <R> R visit(This.Visitor<R> v);
    }
}

Il suo suggerimento non è mai stato realizzato in Java 1.5.0. Pertanto, tutte le altre risposte sono corrette: non vi è alcuna differenza tra le interfacce nidificate statiche e non statiche.


Va notato che tutto ciò si riferisce a GJ che era un processore molto antico per generici in Java.
Marchese di Lorne,

-1

In Java, l'interfaccia / classe statica consente all'interfaccia / classe di essere utilizzata come una classe di livello superiore, ovvero può essere dichiarata da altre classi. Quindi, puoi fare:

class Bob
{
  void FuncA ()
  {
    Foo.Bar foobar;
  }
}

Senza lo statico, quanto sopra non sarebbe in grado di compilare. Il vantaggio è che non è necessario un nuovo file di origine solo per dichiarare l'interfaccia. Associa inoltre visivamente l'interfaccia Bar alla classe Foo poiché devi scrivere Foo.Bar e implica che la classe Foo fa qualcosa con le istanze di Foo.Bar.

Una descrizione dei tipi di classe in Java .


Senza la statica che fa compilazione. Lo "statico" è ridondante.
Marchese di Lorne,

@EJP: Secondo quell'articolo a cui mi sono collegato, senza la statica la classe interna può essere utilizzata solo dalla classe proprietaria e non da qualsiasi cosa al di fuori della classe dovuta. La statica la rende una classe di livello superiore nidificata e può essere utilizzata da oggetti al di fuori dell'ambito della classe proprietaria (almeno, secondo l'articolo). Lo statico non è del tutto ridondante e influisce sull'ambito della classe interna. Questo vale per le classi, le interfacce possono sempre agire come oggetti di livello superiore (è necessario leggere le specifiche del linguaggio per verificare). Forse avrei dovuto separare l'interfaccia e le parti di classe della mia risposta (la mia cattiva).
Skizz,

-6

Statico significa che qualsiasi parte della classe del pacchetto (progetto) può accedervi senza usare un puntatore. Questo può essere utile o difficile a seconda della situazione.

L'esempio perfetto dell'utilità dei metodi "statici" è la classe di matematica. Tutti i metodi in matematica sono statici. Ciò significa che non devi fare nulla, creare una nuova istanza, dichiarare le variabili e archiviarle in ancora più variabili, puoi semplicemente inserire i tuoi dati e ottenere un risultato.

L'elettricità statica non è sempre così utile. Se, ad esempio, stai eseguendo un confronto tra casi, potresti voler archiviare i dati in diversi modi. Non è possibile creare tre metodi statici con firme identiche. Sono necessarie 3 diverse istanze, non statiche, quindi è possibile e confrontare, poiché se sono statiche, i dati non cambieranno insieme all'input.

I metodi statici sono utili per i rendimenti di una volta e per i calcoli rapidi o i dati facilmente ottenibili.


1
So cosa significa statico. Non l'ho mai visto su un'interfaccia prima d'ora. Pertanto la tua domanda è fuori tema - scusa
Mo.

1
Penso che la sua risposta sia fuori tema.
Koray Tugay,
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.