Perché nessun metodo statico nelle interfacce, ma campi statici e classi interne OK? [pre-Java 8] [duplicato]

StackOverflow https://stackoverflow.com/questions/129267

  •  02-07-2019
  •  | 
  •  

Domanda

    

Questa domanda ha già una risposta qui:

         

Qui sono state poste alcune domande sul perché non è possibile definire metodi statici all'interno delle interfacce, ma nessuno di essi risolve un'incoerenza di base: perché è possibile definire campi statici e tipi interni statici all'interno di un'interfaccia, ma non metodi statici ?

I tipi interni statici forse non sono un confronto equo, dal momento che è solo lo zucchero sintattico che genera una nuova classe, ma perché i campi ma non i metodi?

Un argomento contro i metodi statici all'interno delle interfacce è che rompe la strategia di risoluzione della tabella virtuale utilizzata dalla JVM, ma non dovrebbe applicarsi ugualmente ai campi statici, ovvero il compilatore può semplicemente integrarlo?

La coerenza è ciò che desidero, e Java non avrebbe dovuto supportare alcuna statica di qualsiasi forma all'interno di un'interfaccia, oppure dovrebbe essere coerente e consentirle.

È stato utile?

Soluzione

È stata fatta una proposta ufficiale per consentire metodi statici nelle interfacce in Java 7. Questa proposta è stata presentata in Project Coin .

La mia opinione personale è che sia un'ottima idea. Non ci sono difficoltà tecniche nell'implementazione, ed è una cosa molto logica e ragionevole da fare. Ci sono diverse proposte in Project Coin che spero che non diventino mai parte del linguaggio Java, ma questa è una che potrebbe ripulire molte API. Ad esempio, il La classe Collections ha metodi statici per manipolare qualsiasi implementazione List ; quelli potrebbero essere inclusi nell'interfaccia List .


Aggiornamento: nel Java Posse Podcast # 234, Joe D'arcy ha citato brevemente la proposta, affermando che era "complessa". e probabilmente non ce la farebbe con Project Coin.


Aggiornamento: Anche se non sono entrati in Project Coin per Java 7, Java 8 supporta funzioni statiche nelle interfacce.

Altri suggerimenti

Vado con la mia teoria dell'animale domestico con questa, che è che la mancanza di coerenza in questo caso è una questione di convenienza piuttosto che di progettazione o necessità, dal momento che non ho sentito nessuna argomentazione convincente che fosse di quei due.

I campi statici ci sono (a) perché erano lì in JDK 1.0, e molte decisioni ingannevoli sono state prese in JDK 1.0, e (b) i campi finali statici nelle interfacce sono la cosa più vicina che java doveva avere in quel momento.

Le classi interne statiche nelle interfacce erano consentite perché questo è puro zucchero sintattico - la classe interna non è in realtà nulla a che fare con la classe genitore.

Quindi i metodi statici non sono ammessi semplicemente perché non c'è motivo convincente per farlo; la coerenza non è sufficientemente convincente per cambiare lo status quo.

Naturalmente, ciò potrebbe essere consentito nelle future versioni di JLS senza interrompere nulla.

Non ha mai senso dichiarare un metodo statico in un'interfaccia. Non possono essere eseguiti dalla normale chiamata MyInterface.staticMethod (). (EDIT: poiché quest'ultima frase ha confuso alcune persone, chiamando MyClass.staticMethod () esegue esattamente l'implementazione di staticMethod su MyClass, che se MyClass è un'interfaccia non può esistere!) Se le chiami specificando la classe di implementazione MyImplementor.staticMethod () allora devi conoscere la classe attuale, quindi è irrilevante se l'interfaccia la contiene o meno.

Ancora più importante, i metodi statici non vengono mai ignorati e se si tenta di farlo:

MyInterface var = new MyImplementingClass();
var.staticMethod();

le regole per statico dicono che il metodo definito nel tipo dichiarato di var deve essere eseguito. Dal momento che questa è un'interfaccia, questo è impossibile.

Ovviamente puoi sempre rimuovere la parola chiave statica dal metodo. Tutto funzionerà bene. Potrebbe essere necessario eliminare alcuni avvisi se viene chiamato da un metodo di istanza.

Per rispondere ad alcuni dei commenti seguenti, il motivo per cui non puoi eseguire " result = MyInterface.staticMethod () " è che dovrebbe eseguire la versione del metodo definito in MyInterface. Ma non può esserci una versione definita in MyInterface, perché è un'interfaccia. Non ha codice per definizione.

Lo scopo delle interfacce è definire un contratto senza fornire un'implementazione. Pertanto, non è possibile avere metodi statici, poiché dovrebbero avere un'implementazione già nell'interfaccia poiché non è possibile ignorare i metodi statici. Per quanto riguarda i campi, sono consentiti solo campi finali statici, che sono essenzialmente costanti (in 1.5+ puoi anche avere enumerazioni nelle interfacce). Le costanti sono lì per aiutare a definire l'interfaccia senza numeri magici.

A proposito, non è necessario specificare esplicitamente modificatori static final per i campi nelle interfacce, poiché sono consentiti solo i campi final statici.

Questo è un vecchio thread, ma questa è una domanda molto importante per tutti. Da quando l'ho notato solo oggi, quindi sto cercando di spiegarlo in modo più pulito:

Lo scopo principale dell'interfaccia è quello di fornire qualcosa di inattuabile, quindi se forniscono

  

metodi statici da consentire

quindi puoi chiamare quel metodo usando interfaceName.staticMethodName () , ma questo è un metodo non implementato e non contiene nulla. Quindi è inutile consentire metodi statici. Pertanto non lo forniscono affatto.

  

i campi statici sono ammessi

poiché i campi non sono implementabili, con l'implementazione intendo che non è possibile eseguire alcuna operazione logica in campo, è possibile eseguire operazioni sul campo. Quindi non stai cambiando il comportamento del campo ed è per questo che sono ammessi.

  

Le classi interne sono consentite

Le classi interne sono consentite perché dopo la compilazione vengono creati diversi file di classe della classe Inner dire InterfaceName $ InnerClassName.class , quindi in pratica si fornisce l'implementazione in entità diverse tutte insieme ma non nell'interfaccia. Quindi viene fornita l'implementazione nelle classi interne.

Spero che questo possa aiutare.

In realtà a volte ci sono ragioni per cui qualcuno può beneficiare di metodi statici. Possono essere utilizzati come metodi di fabbrica per le classi che implementano l'interfaccia. Ad esempio, questo è il motivo per cui ora abbiamo l'interfaccia Collection e la classe Collections in openjdk. Quindi ci sono soluzioni alternative come sempre: fornire a un'altra classe un costruttore privato che fungerà da "spazio dei nomi" per i metodi statici.

Prima di Java 5, un uso comune per i campi statici era:

interface HtmlConstants {
    static String OPEN = "<";
    static String SLASH_OPEN = "</";
    static String CLOSE = ">";
    static String SLASH_CLOSE = " />";
    static String HTML = "html";
    static String BODY = "body";
    ...
}

public class HtmlBuilder implements HtmlConstants { // implements ?!?
    public String buildHtml() {
       StringBuffer sb = new StringBuffer();
       sb.append(OPEN).append(HTML).append(CLOSE);
       sb.append(OPEN).append(BODY).append(CLOSE);
       ...
       sb.append(SLASH_OPEN).append(BODY).append(CLOSE);
       sb.append(SLASH_OPEN).append(HTML).append(CLOSE);
       return sb.toString();
    }
}

Ciò significava che HtmlBuilder non avrebbe dovuto qualificare ciascuna costante, quindi poteva usare OPEN invece di HtmlConstants.OPEN

L'uso degli attrezzi in questo modo è in definitiva fonte di confusione.

Ora con Java 5, abbiamo la sintassi import static per ottenere lo stesso effetto:

private final class HtmlConstants {
    ...
    private HtmlConstants() { /* empty */ }
}

import static HtmlConstants.*;
public class HtmlBuilder { // no longer uses implements
    ...
}

Non c'è alcun motivo reale per non avere metodi statici nelle interfacce tranne: i progettisti del linguaggio Java non lo volevano così. Dal punto di vista tecnico sarebbe logico consentirli. Dopotutto anche una classe astratta può averli. Presumo ma non l'ho provato, che puoi "fare a mano" codice byte in cui l'interfaccia ha un metodo statico e dovrebbe funzionare senza problemi per chiamare il metodo e / o utilizzare l'interfaccia come al solito.

Mi chiedo spesso perché i metodi statici? Hanno i loro usi, ma i metodi a livello di pacchetto / spazio dei nomi coprirebbero probabilmente 80 dei metodi statici utilizzati.

Mi vengono in mente due ragioni principali:

  1. I metodi statici in Java non possono essere sovrascritti da sottoclassi, e questo è un affare molto più grande per i metodi rispetto ai campi statici. In pratica, non ho mai nemmeno voluto scavalcare un campo in una sottoclasse, ma ho sempre la precedenza sui metodi. Quindi avere metodi statici impedisce a una classe che implementa l'interfaccia di fornire la propria implementazione di quel metodo, il che vanifica ampiamente lo scopo di usare un'interfaccia.

  2. Le interfacce non devono avere codice; ecco a cosa servono le classi astratte. Il punto centrale di un'interfaccia è quello di farti parlare di oggetti probabilmente non correlati che accadono a tutti con un certo set di metodi. In realtà, fornire un'implementazione di questi metodi è al di fuori dei limiti di ciò che le interfacce intendono essere.

I metodi statici sono legati a una classe. In Java, un'interfaccia non è tecnicamente una classe, è un tipo, ma non una classe (quindi, la parola chiave implementa e le interfacce non estendono Object). Poiché le interfacce non sono classi, non possono avere metodi statici, poiché non esiste alcuna classe effettiva a cui collegarsi.

È possibile chiamare InterfaceName.class per ottenere l'oggetto Class corrispondente all'interfaccia, ma la classe Class afferma specificamente che rappresenta le classi e le interfacce in un'applicazione Java. Tuttavia, l'interfaccia stessa non viene trattata come una classe e quindi non è possibile collegare un metodo statico.

In un'interfaccia possono essere dichiarati solo campi finali statici (molto simili ai metodi, che sono pubblici anche se non includi la parola chiave "quot" pubblica ", i campi statici sono" final "con o senza la parola chiave).

Questi sono solo valori e verranno copiati letteralmente ovunque vengano utilizzati al momento della compilazione, quindi in realtà non puoi mai "chiamare" campi statici in fase di esecuzione. Avere un metodo statico non avrebbe la stessa semantica, poiché implicherebbe la chiamata di un'interfaccia senza un'implementazione, che Java non consente.

Il motivo è che tutti i metodi definiti in un'interfaccia sono astratti indipendentemente dal fatto che tu dichiari o meno esplicitamente quel modificatore. Un metodo statico astratto non è una combinazione consentita di modificatori poiché i metodi statici non possono essere ignorati.

Sul motivo per cui le interfacce consentono campi statici. Ho la sensazione che dovrebbe essere considerata una "caratteristica". L'unica possibilità che mi viene in mente sarebbe quella di raggruppare le costanti a cui sarebbero interessate le implementazioni dell'interfaccia.

Sono d'accordo che la coerenza sarebbe stata un approccio migliore. Nessun membro statico dovrebbe essere consentito in un'interfaccia.

Credo che sia possibile accedere ai metodi statici senza creare un oggetto e l'interfaccia non consente la creazione di un oggetto in modo da impedire ai programmatori di utilizzare direttamente i metodi dell'interfaccia piuttosto che dalla sua classe implementata. Ma se si definisce un metodo statico in un'interfaccia, è possibile accedervi direttamente senza la sua implementazione. Pertanto i metodi statici non sono ammessi nelle interfacce. Non penso che la coerenza dovrebbe essere una preoccupazione.

Il metodo statico dell'interfaccia Java 1.8 è visibile solo ai metodi di interfaccia, se rimuoviamo il metodo methodSta1 () dalla classe InterfaceExample, non riusciremo a usarlo per l'oggetto InterfaceExample. Tuttavia, come altri metodi statici, possiamo usare metodi statici di interfaccia usando il nome della classe. Ad esempio, un'istruzione valida sarà: exp1.methodSta1 ();

Quindi dopo aver guardato sotto l'esempio possiamo dire: 1) Il metodo statico dell'interfaccia Java fa parte dell'interfaccia, non possiamo usarlo per oggetti di classe di implementazione.

2) I metodi statici dell'interfaccia Java sono utili per fornire metodi di utilità, ad esempio controllo null, ordinamento delle raccolte, registro ecc.

3) Il metodo statico dell'interfaccia Java ci aiuta a fornire sicurezza impedendo alle classi di implementazione (InterfaceExample) di sovrascriverle.

4) Non possiamo definire il metodo statico dell'interfaccia per i metodi della classe Object, otterremo un errore del compilatore come & # 8220; Questo metodo statico non può nascondere il metodo dell'istanza da Object & # 8221 ;. Questo perché non è consentito in Java, poiché Object è la classe di base per tutte le classi e non possiamo avere un metodo statico a livello di classe e un altro metodo di istanza con la stessa firma.

5) Possiamo usare i metodi statici dell'interfaccia java per rimuovere le classi di utilità come Collezioni e spostare tutti i metodi statici sull'interfaccia corrispondente,    sarebbe facile da trovare e utilizzare.

public class InterfaceExample implements exp1 {

    @Override
    public void method() {
        System.out.println("From method()");
    }

    public static void main(String[] args) {
        new InterfaceExample().method2();
        InterfaceExample.methodSta2();      //  <---------------------------    would not compile
        // methodSta1();                        //  <---------------------------    would not compile
        exp1.methodSta1();
    }

    static void methodSta2() {          //          <-- it compile successfully but it can't be overridden in child classes
        System.out.println("========= InterfaceExample :: from methodSta2() ======");
    }
}


interface exp1 {

    void method();
    //protected void method1();         //          <--      error
    //private void method2();           //          <--      error
    //static void methodSta1();         //          <--      error it require body in java 1.8

    static void methodSta1() {          //          <-- it compile successfully but it can't be overridden in child classes
        System.out.println("========= exp1:: from methodSta1() ======");
    }

    static void methodSta2() {          //          <-- it compile successfully but it can't be overridden in child classes
        System.out.println("========= exp1:: from methodSta2() ======");
    }

    default void method2() { System.out.println("---  exp1:: from method2() ---");}
    //synchronized default void method3() { System.out.println("---");}             // <-- Illegal modifier for the interface method method3; only public, abstract, default, static 
                                                                                // and strictfp are permitted
    //final default void method3() { System.out.println("---");} //             <--      error
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top