L '"interfaccia statica" è una buona pratica?


13

Ho appena notato che esiste un'opzione per avere metodi statici nelle interfacce. Come per i campi statici di interfaccia, c'è un comportamento interessante: questi non sono ereditati.

Non sono sicuro che sia utile nelle interfacce reali che devono essere implementate. Tuttavia, consente al programmatore di creare interfacce che sono solo buste per roba statica, come ad esempio le classi di utilità.

Un semplice esempio è solo una busta per le costanti globali. Rispetto a una classe, puoi facilmente notare la piastra di caldaia mancante di public static finalcome si presume (rendendola meno dettagliata).

public interface Constants {
    String LOG_MESSAGE_FAILURE = "I took an arrow to the knee.";
    int DEFAULT_TIMEOUT_MS = 30;
}

Potresti anche rendere qualcosa di più complesso, come questo pseudo-enum di tasti di configurazione.

public interface ConfigKeys {
    static createValues(ConfigKey<?>... values) {
        return Collections.unmodifiableSet(new HashSet(Arrays.asList(values)));
    }

    static ConfigKey<T> key(Class<T> clazz) {
        return new ConfigKey<>(clazz);
    }

    class ConfigKey<T> {
        private final Class<T> type;
        private ConfigKey(Class<T> type) {
            this.type = type;
        }
        private Class<T> getType() {
            return type;
        }
    }
}

import static ConfigKeys.*;
public interface MyAppConfigKeys {
    ConfigKey<Boolean> TEST_MODE = key(Boolean.class);
    ConfigKey<String> COMPANY_NAME = key(String.class);

    Set<ConfigKey<?>> VALUES = createValues(TEST_MODE, COMPANY_VALUE);

    static values() {
        return VALUES;
    }
}

In questo modo è anche possibile creare alcune "classi" di utilità. Tuttavia, nelle utility, è spesso utile utilizzare metodi di supporto privati ​​o protetti, che non è del tutto possibile nelle classi.

La considero una bella nuova funzionalità, e soprattutto il fatto che i membri statici non siano ereditati è un concetto interessante che è stato introdotto solo per le interfacce.

Mi chiedo se puoi considerarlo una buona pratica. Mentre lo stile del codice e le migliori pratiche non sono assiomatici e c'è spazio per l'opinione, penso che di solito ci siano ragioni valide a sostegno dell'opinione.

Sono più interessato ai motivi (non) per usare modelli come questi due.


Nota che non intendo implementare quelle interfacce. Sono semplicemente una busta per il loro contenuto statico. Intendo solo usare le costanti o i metodi e possibilmente usare l'importazione statica.


1
La mia reazione istintiva a questo è che può solo portare a cose cattive. Anche se i metodi statici pubblici sono essenzialmente funzioni globali spaziate dai nomi. Sembra che consentire qualsiasi codice di implementazione in un'interfaccia sia contrario al suo scopo in quanto una definizione del contratto è assente. Dico lasciare un'implementazione parziale alle classi astratte. Dovrò leggere il motivo per cui questo è stato aggiunto.
Adrian,

Devo andare per ora, ma scriverò qualcosa domani su di esso. Il fatto è che prima di un'interfaccia aveva uno scopo chiaro che era chiaramente separato dallo scopo di una AbstractClass. Ora si sovrappongono pesantemente in un modo che viola la definizione agnostica del linguaggio di un'interfaccia. Inoltre, in alcuni casi non è più possibile ereditare da più interfacce se implementano metodi predefiniti. Quindi ora ci sono eccezioni all'ereditarietà di più interfacce in cui non ce n'era nessuna. Poiché Java si è discostato drasticamente dalla definizione comune di un'interfaccia ...
Adrian,

potrebbe anche confondere i nuovi sviluppatori che stanno cercando di ottenere una corretta gestione dei concetti fondamentali di OOP come Interfaces, AbstractClases, quando usare la composizione anziché l'eredità, ... ecc.
Adrian,

Altrimenti, stai parlando di defaultmetodi. Sto parlando di staticmetodi e campi. Quelli non sono ereditati, quindi non rompono l'eredità multipla.
Vlasec,

Risposte:


1

Un semplice esempio è solo una busta per le costanti globali.

L'inserimento di costanti nelle interfacce è chiamato Constant Interface Antipatternpoiché le costanti sono spesso semplicemente un dettaglio di implementazione. Ulteriori inconvenienti sono riassunti nell'articolo di Wikipedia sull'interfaccia di Constant .

In questo modo è anche possibile creare alcune "classi" di utilità.

In effetti il documento Java afferma che i metodi statici possono semplificare l'organizzazione dei metodi di supporto, ad esempio quando si chiamano metodi di supporto da metodi predefiniti.


1
Non penso che questa sia in realtà una risposta alla domanda posta dal momento che potresti fare entrambe queste cose senza questa nuova funzionalità.
Adrian,

Ah, quindi dalla documentazione, è inteso come un metodo di supporto. Tuttavia, è anche pubblico, il che lo contraddice. Quindi forse è pensato anche per aiutare le classi che implementano l'interfaccia. Ma non sono ereditati, quindi dovresti usare l'importazione statica per farti sentire come se lo avessi ereditato. Bene, grazie per averlo cercato nei documenti.
Vlasec,

2
Non c'è assolutamente nulla di sbagliato nel mettere le costanti nelle interfacce, se quelle costanti sono in qualche modo parte dell'API descritta dall'interfaccia. L'unica cosa che è un antipattern è mettere costanti in interfacce senza metodi e avere classi implementano l'interfaccia solo per fare riferimento alle costanti senza prefisso di classe. È un abuso di interfacce e, dall'introduzione delle importazioni statiche, completamente obsoleto.
Michael Borgwardt,

1

Come indicato nella domanda, l'interfaccia non è pensata per essere implementata (o istanziata). Quindi potremmo confrontarlo con una classe che ha un costruttore privato:

  • La classe non può essere estesa senza super()chiamare, ma l'interfaccia può essere "implementata"
  • La classe non può essere istanziata senza riflessioni, mentre l'interfaccia può essere facilmente istanziata come una classe anonima semplice come questa: new MyInterface() {};

Questo confronto è a favore della classe in quanto consente un maggiore controllo. Detto questo, la classe non vuole essere un detentore di roba statica, ci si aspetterebbe che abbia delle istanze. Bene, non esiste un'opzione perfetta.

C'è anche una cosa strana nell'eredità dall'interfaccia statica:

  • Una classe di "implementazione" eredita i campi, ma non eredita i metodi statici
  • Un'interfaccia estesa non eredita affatto membri statici
  • (Mentre una classe che estende una classe eredita ogni membro non privato ... e poiché non può essere estesa da un'interfaccia, c'è meno spazio per la confusione)

Questi potrebbero essere motivi per considerarlo un modello errato e continuare a utilizzare le classi statiche per questi casi. Tuttavia, per chi è dipendente dallo zucchero sintattico può essere comunque allettante anche con i lati negativi.


... Comunque, le convenzioni di codifica di una squadra possono coprire quei problemi. Per me, anche se non lega la mano di un programmatore quasi quanto una classe con un costruttore privato, comporta comunque meno rischi rispetto ai setter e i setter vengono spesso usati.
Vlasec,

0

Come @MarkusPscheidt questa idea è essenzialmente un'estensione della Constant Interface . Questo approccio è stato inizialmente ampiamente utilizzato per consentire a un singolo set di costanti di essere ereditate da molte classi disparate. Il motivo per cui questo è finito per essere considerato un antipattern è stato a causa dei problemi che sono stati riscontrati usando questo approccio in progetti reali. Non vedo alcun motivo per pensare che questi problemi riguarderebbero solo le costanti.

Probabilmente ancora più importante, non c'è davvero bisogno di farlo. Usa invece le importazioni statiche e sii esplicito su cosa stai importando e da dove.


Sì, le importazioni statiche sembrano un'idea migliore dell'ereditarietà delle costanti e funziona con i membri statici di entrambe le classi e interfacce. Ed è visibile solo all'interno della classe / interfaccia.
Vlasec,

@Vlasec Giusto ma non è necessario farlo su un'interfaccia. L'unica differenza tra farlo con una classe e un'interfaccia è che puoi ereditare da più di un'interfaccia. La pratica standard per una classe di utilità come questa è quella di rendere privato il costruttore per impedire l'istanza o l'estensione. Non c'è alcun vantaggio nel farlo in un'interfaccia su una classe. Si apre semplicemente la porta per un uso improprio.
JimmyJames il

Riesco a vedere il valore nelle costanti sulle interfacce. Le interfacce tendono a definire metodi e metodi che a volte accettano costanti come parametri. Ad esempio: interface ColorFilter {Image applyGradient(int colorSpace, int width, byte[] pixels); }. Posso immaginare che ci sia alcune costanti diverse RGB, RGBA, CMYK, GREYSCALE, INDEXEDecc
Stijn de Witt

@StijndeWitt Questa non sarebbe la cosa peggiore, ma puoi usare una enum o una classe nidificata sull'interfaccia per questo scopo. Il vero problema sta usando questo per portare la statica nell'ambito di molte classi attraverso l'ereditarietà. Tale approccio è chiaramente inferiore all'utilizzo delle importazioni statiche.
JimmyJames

0

Direi che, se sei interessato a utilizzare correttamente la nuova funzionalità, dai un'occhiata al codice nel JDK. I metodi predefiniti sulle interfacce sono molto usati e facili da trovare, ma i metodi statici sulle interfacce sembrano essere molto rari. Alcuni esempi includono java.time.chrono.Chronology, java.util.Comparator, java.util.Map, e un paio di interfacce del tutto in java.util.functione java.util.stream.

La maggior parte degli esempi che ho esaminato riguarda l'uso di quelle API che sono probabilmente molto comuni: la conversione tra diversi tipi nell'API temporale, ad esempio, per confronti che possono essere fatti frequentemente tra oggetti passati a funzioni o oggetti contenuti in collezioni. Il mio asporto: se c'è una parte della tua API che deve essere flessibile, ma puoi coprire l'80% dei casi d'uso con una manciata di valori predefiniti, considera l'utilizzo di metodi statici sulle tue interfacce. Data la scarsa frequenza con cui questa funzionalità sembra essere utilizzata nel JDK, sarei molto conservatore nel farne uso nel mio codice.

I tuoi esempi non assomigliano per niente a quello che vedo nel JDK. Per quei casi specifici, dovresti quasi sicuramente creare un enumche implementa l'interfaccia desiderata. Un'interfaccia descrive una serie di comportamenti e non ha davvero alcuna attività commerciale che mantiene una raccolta delle sue implementazioni.


-1

Mi chiedo se ci sono ragioni per non usarlo.

Che ne dici, um, compatibilità con le JVM più vecchie?

Consideri questa una buona idea in generale?

No. È una modifica incompatibile all'indietro che aggiunge zilch all'espressività della lingua. Due pollici in giù.

Ci sono alcune situazioni modello in cui consigli vivamente le lezioni?

Consiglio vivamente di utilizzare le classi per contenere i dettagli di implementazione. Un'interfaccia vuole dire semplicemente "questo oggetto supporta le operazioni XYZ" e non ha nulla a che fare con i metodi statici che potresti pensare di inserire nella dichiarazione dell'interfaccia. Questa funzione è come una poltrona reclinabile con un mini frigorifero incorporato. Certo ha alcuni usi di nicchia ma non tira abbastanza peso da meritare di essere mainstream.

AGGIORNAMENTO: per favore non più downvotes. Chiaramente alcune persone pensano che i metodi statici nelle interfacce siano le ginocchia delle api, e con la presente sto capitolando. Appena cancellerei questa risposta, ma i commenti allegati sono una discussione interessante.


I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
maple_shaft

Se declassassi la tua domanda, lo farei perché le nostre prime due risposte non sono vere risposte. La nuova versione del linguaggio offre nuovi strumenti per semplificare le cose, questo è lo scopo di queste nuove versioni. La terza risposta è un po 'persa tra le macerie dei primi due sballati.
Vlasec,
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.