Domanda

Metodi predefiniti sono un bel nuovo strumento nella nostra casella degli strumenti Java.Tuttavia, ho provato a scrivere un'interfaccia che definisce una versione default del metodo toString.Java mi dice che questo è proibito, dal momento che i metodi dichiarati in java.lang.Object potrebbero non essere defaulted.Perché è così il caso?

So che esiste la regola "Classe base vince sempre", quindi per impostazione predefinita (PUN;), qualsiasi implementazione default di un metodo Object verrà sovrascritta dal metodo da Object comunque.Tuttavia, non vedo alcuna ragione per cui non ci dovrebbe essere un'eccezione per i metodi da Object nella specifica.Soprattutto per toString potrebbe essere molto utile avere un'implementazione predefinita.

Allora, qual è il motivo per cui i progettisti Java hanno deciso di non consentire i metodi default Metodi sovrascrivibili da Object?

È stato utile?

Soluzione

Questo è ancora un altro di quei problemi di progettazione linguistica che sembra "ovviamente una buona idea" finché non inizi a scavare e ti rendi conto che è in realtà una cattiva idea.

Questa mail ha molto Il soggetto (e su altri soggetti anche) c'erano diverse forze di progettazione che convergono per portarci al design attuale:

    .
  • il desiderio di mantenere il modello di ereditariele semplice;
  • il fatto che una volta che si guarda oltre gli esempi ovvi (ad esempio, girando AbstractList in un'interfaccia), si rende conto che l'ereditarizzazione di uguali / HashCode / Tostring è fortemente legato a singola ereditarietà e stato, e le interfacce sono moltiplicate ereditate e apolidi; / li >.
  • che ha potenzialmente aperto la porta ad alcuni comportamenti sorprendenti.

Hai già toccato il "Tenere il suo semplice" obiettivo; Le regole di ereditarietà e di risoluzione dei conflitti sono progettate per essere molto semplici (le classi vincono le interfacce, le interfacce derivate vincono su superinterfacce e qualsiasi altro conflitto viene risolto dalla classe di implementazione.) Naturalmente queste regole potrebbero essere modificate per fare un'eccezione, ma Penso che troverai quando inizi a tirare su quella stringa, che la complessità incrementale non è piccola come potresti pensare.

Certo, c'è un certo grado di beneficio che giustificherebbe più complessità, ma in questo caso non è lì. I metodi di cui parliamo qui sono uguali, hashcode e tostring. Questi metodi sono tutti intrinsecamente sullo stato dell'oggetto, ed è la classe che possiede lo stato, non l'interfaccia, che è nella migliore posizione per determinare quali mezzi di uguaglianza per quella classe (soprattutto come il contratto di uguaglianza è abbastanza forte; vedere efficace Java per alcune conseguenze sorprendenti); Gli scrittori di interfaccia sono troppo lontani.

è facile estrarre l'esempio AbstractList; Sarebbe bello se potessimo sbarazzarlo del AbstractList e mettere il comportamento nell'interfaccia List. Ma una volta che ti muovi oltre questo esempio ovvio, non ci sono molti altri buoni esempi da trovare. A root, AbstractList è progettato per singola ereditarietà. Ma le interfacce devono essere progettate per più ereditarietà.

Inoltre, immagina di scrivere questa classe:

class Foo implements com.libraryA.Bar, com.libraryB.Moo { 
    // Implementation of Foo, that does NOT override equals
}
.

Lo scrittore Foos guarda ai SuperTypes, non vede l'attuazione di uguali e conclude che per ottenere l'uguaglianza di riferimento, tutto ciò che ha bisogno è eredita è uguale da Object. Quindi, la prossima settimana, il manutentore della biblioteca per la barra "Helplyly" aggiunge un'implementazione predefinita equals. Ooops! Ora la semantica di Foo è stata interrotta da un'interfaccia in un altro dominio di manutenzione "Aiutamente" aggiungendo un valore predefinito per un metodo comune.

I valori predefiniti dovrebbero essere predefiniti. Aggiunta di un predefinito a un'interfaccia in cui non c'era nessuno (in nessun punto della gerarchia) non dovrebbe influire sulla semantica delle classi di implementazione concretinenti. Ma se i valori predefiniti potrebbero "sovrascrivere" i metodi dell'oggetto, non sarebbe vero.

Così, mentre sembra una caratteristica innocua, è infatti abbastanza dannosa: aggiunge molta complessità per la piccola espressività incrementale, e lo rende troppo facile per le modifiche ben intenzionate e dall'aspetto innocuo in base a separatamente compilate interfacce per minare la semantica desiderata delle classi di implementazione.

Altri suggerimenti

It is forbidden to define default methods in interfaces for methods in java.lang.Object, since the default methods would never be "reachable".

Default interface methods can be overwritten in classes implementing the interface and the class implementation of the method has a higher precedence than the interface implementation, even if the method is implemented in a superclass. Since all classes inherit from java.lang.Object, the methods in java.lang.Object would have precedence over the default method in the interface and be invoked instead.

Brian Goetz from Oracle provides a few more details on the design decision in this mailing list post.

I do not see into the head of Java language authors, so we may only guess. But I see many reasons and agree with them absolutely in this issue.

The main reason for introducing default methods is to be able to add new methods to interfaces without breaking the backward compatibility of older implementations. The default methods may also be used to provide "convenience" methods without the necessity to define them in each of the implementing classes.

None of these applies to toString and other methods of Object. Simply put, default methods were designed to provide the default behavior where there is no other definition. Not to provide implementations that will "compete" with other existing implementations.

The "base class always wins" rule has its solid reasons, too. It is supposed that classes define real implementations, while interfaces define default implementations, which are somewhat weaker.

Also, introducing ANY exceptions to general rules cause unnecessary complexity and raise other questions. Object is (more or less) a class as any other, so why should it have different behaviour?

All and all, the solution you propose would probably bring more cons than pros.

The reasoning is very simple, it is because Object is the base class for all the Java classes. So even if we have Object's method defined as default method in some interface, it will be useless because Object's method will always be used. That is why to avoid confusion, we cannot have default methods that are overriding Object class methods.

To give a very pedantic answer, it is only forbidden to define a default method for a public method from java.lang.Object. There are 11 methods to consider, which can be categorized in three ways to answer this question.

  1. Six of the Object methods cannot have default methods because they are final and cannot be overridden at all: getClass(), notify(), notifyAll(), wait(), wait(long), and wait(long, int).
  2. Three of the Object methods cannot have default methods for the reasons given above by Brian Goetz: equals(Object), hashCode(), and toString().
  3. Two of the Object methods can have default methods, though the value of such defaults is questionable at best: clone() and finalize().

    public class Main {
        public static void main(String... args) {
            new FOO().clone();
            new FOO().finalize();
        }
    
        interface ClonerFinalizer {
            default Object clone() {System.out.println("default clone"); return this;}
            default void finalize() {System.out.println("default finalize");}
        }
    
        static class FOO implements ClonerFinalizer {
            @Override
            public Object clone() {
                return ClonerFinalizer.super.clone();
            }
            @Override
            public void finalize() {
                ClonerFinalizer.super.finalize();
            }
        }
    }
    
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top