Interfaccia finale in Java?

Un’interfaccia può essere dichiarata definitiva in Java?

Le interfacce sono al 100% astratte e l’unico modo per creare un’istanza di un’interfaccia è istanziare una class che la implementa. Consentire che le interfacce siano final è completamente inutile.

EDIT Le domande non sono così oltraggiose come pensavo. Un’interfaccia finale è quella che non può essere estesa da altre interfacce ma può essere implementata apparentemente sensata.

Potrei pensare ad una differenza tra una class finale e un’interfaccia finale. Estendere una class può comprometterne l’integrità perché contiene uno stato. L’estensione di un’interfaccia aggiunge semplicemente operazioni e non può compromettere l’integrità dell’implementazione perché l’interfaccia è stateless.

No. Il tentativo di dichiarare un’interfaccia come finale in Java ha come risultato un errore di compilazione. Questa è una decisione sul design del linguaggio: le interfacce Java sono pensate per essere estese.

No. La sezione relativa alla specifica del linguaggio Java 9.1.1. Modificatori di interfaccia afferma quanto segue:

Una dichiarazione di interfaccia può includere modificatori di interfaccia.

 InterfaceModifier: (one of) Annotation public protected private abstract static strictfp 

Come si può vedere, l’elenco non include la final .

Perché il linguaggio è stato progettato in questo modo?

Se un’interfaccia è stata dichiarata final suppongo che avrebbe potuto significare questo

  • Nessuna altra interfaccia potrebbe estenderlo

    Questa sarebbe una restrizione non sensata. I motivi per cui può essere utile dichiarare una finale di class è proteggere gli invarianti di stato, proibire l’override di tutti i metodi contemporaneamente, ecc. Nessuna di queste restrizioni ha senso per le interfacce. (Non esiste uno stato e tutti i metodi devono essere ignorati).

  • Nessuna class potrebbe implementare l’interfaccia

    Questo ovviamente sconfigge completamente lo scopo di un’interfaccia.

Da Java Language Specification (Third Edition):

9.1.1.1 Interfacce astratte

Ogni interfaccia è implicitamente abstract . Questo modificatore è obsoleto e non dovrebbe essere usato in nuovi programmi.

Quindi, abstract + final è una sorta di ossimoro.

L’ho provato e apparentemente puoi creare un’interfaccia finale in java. Non ho idea del perché lo faresti, ma puoi farlo. Questo è come l’ho fatto.

  1. Compilare un’interfaccia non finale. Ho salvato il codice seguente in FinalInterface.java. Poi l’ho compilato.

    interfaccia FinalInterface {}

  2. Esegui BCELifier su di esso. Questo ha creato un file chiamato FinalInterfaceCreator.java

  3. Modificalo. Cerca una riga simile a quella di seguito e aggiungi ACC_FINAL.

    _cg = new ClassGen (“FinalInterface”, “java.lang.Object”, “FinalInterface.java”, ACC_INTERFACE | ACC_ABSTRACT | ACC_FINAL, new String [] {});

  4. Compilare ed eseguire FinalInterfaceCreator.java modificato. Questo dovrebbe sovrascrivere il file FinalInterface.class originale con uno nuovo simile ma definitivo.

Per testarlo, ho creato due nuovi file java TestInterface e TestClass. TestInterface è un’interfaccia che estende FinalInterface e TestClass è una class che implementa FinalInterface. Il compilatore si è rifiutato di compilare perché FinalInterface è definitivo.

 TestClass.java:2: cannot inherit from final FinalInterface class TestClass implements FinalInterface TestInterface.java:2: cannot inherit from final FinalInterface interface TestInterface extends FinalInterface 

Inoltre, ho provato a creare un’istanza di FinalInterface usando i proxy dinamici

 class Main { public static void main ( String [ ] args ) { Class < ? > ntrfc = FinalInterface . class ; ClassLoader classLoader = ntrfc . getClassLoader ( ) ; Class < ? > [ ] interfaces = { ntrfc } ; java . lang . reflect . InvocationHandler invocationHandler = new java . lang . reflect . InvocationHandler ( ) { public Object invoke ( Object proxy , java . lang . reflect . Method method , Object [ ] args ) { return ( null ) ; } } ; FinalInterface fi = ( FinalInterface ) ( java . lang . reflect . Proxy . newProxyInstance ( classLoader , interfaces , invocationHandler ) ) ; } } 

Questo è stato compilato ma non è stato eseguito

 Exception in thread "main" java.lang.ClassFormatError: Illegal class modifiers in class FinalInterface: 0x610 at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:632) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at java.net.URLClassLoader.defineClass(URLClassLoader.java:277) at java.net.URLClassLoader.access$000(URLClassLoader.java:73) at java.net.URLClassLoader$1.run(URLClassLoader.java:212) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:205) at java.lang.ClassLoader.loadClass(ClassLoader.java:319) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294) at java.lang.ClassLoader.loadClass(ClassLoader.java:264) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:332) at Main.main(Main.java:6) 

Quindi le prove suggeriscono che è ansible creare un’interfaccia finale in java, ma perché vorresti farlo?

Mentre un’interfaccia finale avrebbe ancora degli usi, nessuno di questi è ampiamente considerato una buona pratica.

Potrebbe essere utilizzata un’interfaccia finale

  • definizione delle costanti. Generalmente considerata una ctriggers idea.
  • meta-informazioni da esaminare attraverso la riflessione, ad esempio un descrittore del pacchetto
  • raggruppando molte classi interne pubbliche in un unico file. (Suggerisco solo che questo sia usato quando si taglia e incolla un codice di esempio che ha molte classi, in quanto si risparmia la seccatura di creare un file per ogni class, le classi interne sono implicitamente statiche)

Puoi fare tutte queste cose con un’interfaccia non finale e contrassegnare un’interfaccia come finale non sarebbe utile come un commento che dice che stai utilizzando un’interfaccia per uno scopo incedental e perché lo stai facendo

Quando si progettano le API in Java, a volte trovo una situazione in cui un tipo nell’API deve essere un’interfaccia Java, ma il numero delle sue implementazioni deve essere limitato – ad esempio solo le API interne possono implementarlo, non chiunque altro.

Mi sono reso conto che ora posso (a partire da Java 1.6) limitare chi implementa un’interfaccia con l’aiuto di processori di annotazione ( articolo su apidesign.org ). Questo è il codice:

 package org.apidesign.demo.finalinterface; import java.util.Collection; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; import org.openide.util.lookup.ServiceProvider; @ServiceProvider(service = Processor.class) @SupportedAnnotationTypes("*") public final class FinalEnforcingProcessor extends AbstractProcessor { @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { checkForViolations(roundEnv.getRootElements()); return true; } private void checkForViolations(Collection all) { for (Element e : all) { if (e instanceof TypeElement) { TypeElement te = (TypeElement) e; /* exception for the only known implementation: if ("org.apidesign.demo.finalinterface.AllowedImplementationTest".equals( te.getQualifiedName().toString()) ) continue; */ for (TypeMirror m : te.getInterfaces()) { if (FinalInterface.class.getName().equals(m.toString())) { processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR, "Cannot implement FinalInterface", e ); } } } checkForViolations(e.getEnclosedElements()); } } } 

Ogni volta che qualcuno include il tuo API JAR su classpath e tenta di compilarlo, il compilatore Javac invoca il tuo processore di annotazioni e ti consente di fallire la compilazione se rileva che qualcun altro sta cercando di implementare la tua interfaccia.

Continuo a preferire l’utilizzo della class finale per l’ API client , ad esempio un’API a cui altri utenti sono autorizzati a chiamare, ma quando non è ansible, il trucco con il processore di annotazione è un’alternativa accettabile.

finale significa che non può essere modificato. Se si tratta di una class, non può essere modificata estendendo (i metodi possono essere modificati eseguendo l’override, gli attributi non possono essere modificati, gli attributi / metodi aggiunti sono accessibili solo come membri del “tipo sottoclass”); se è una variabile (attributo / locale), il valore non può essere modificato dopo il primo incarico; se è un metodo, l’implementazione non può essere modificata (un metodo finale può essere sovraccaricato ma non può essere sovrascritto). Non ha senso dichiarare un’interfaccia finale poiché normalmente non c’è alcuna implementazione da modificare (i metodi di interfaccia statici e predefiniti non possono essere definitivi).

Invece di dichiarare l’interfaccia come finale, possiamo evitare di creare un object di interfaccia.

Punto di creazione dell’interfaccia per implementare i suoi metodi tramite la sottoclass. Se stiamo sconfiggendo questo scopo, ritengo che sia infondato.

Se qualcuno ha altri suggerimenti, cortesemente faccelo sapere

Ogni volta che crei un’annotazione, stai creando un’interfaccia che in qualche modo è effettivamente definitiva.

Un’interfaccia che non ha membri è nota come interfaccia marcatore o taggata. Ad esempio: Serializable, Cloneable, Remote ecc. Vengono utilizzati per fornire alcune informazioni essenziali alla JVM in modo che JVM possa eseguire alcune operazioni utili.