2011-10-25 15 views
6

Ho le seguenti classi di esempio in Java:Impossibile chiamare direttamente il costruttore supertipo: perché no?

errore
public class A { } 

public class Super { 
    protected Super() { } 
    public Super(A a) { } 
} 

public class Sub extends Super { } 

public class Consumer { 
    public Consumer() { 
     Sub sub = new Sub(new A()); //compiler error 
    } 
} 

Il compilatore afferma che gli argomenti non possono essere applicati al costruttore predefinito in Sub, che è perfettamente comprensibile.

Ciò di cui sono curioso è la logica alla base di questa decisione. Java genera il costruttore vuoto predefinito in Sub; perché non può chiamarlo dietro le quinte in questo caso? Questo è principalmente un caso di sana gestione della mano, o c'è una ragione tecnica?

EDIT

Sono consapevole che questa è una limitazione della lingua. Sono curioso di perché è un limite di lingua.

EDIT 2

Sembra che, come spesso accade, ero troppo vicino al codice che è stato effettivamente lavorando per vedere il quadro generale. Ho pubblicato un contro-esempio nelle risposte qui sotto che mostra perché questa è una cosa cattiva.

+0

Tutto quello che riuscivo a trovare nelle specifiche era che non si poteva fare. Nessuna spiegazione sul perché http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.2 –

+1

Ecco l'interpretazione di Jon Skeet su http://stackoverflow.com/questions/1644317/java -costruttore-ereditarietà/1644410 # 1644410 –

+1

Sono confuso sulla tua modifica. Vi state chiedendo perché, quando chiamate un costruttore con un argomento, non è possibile chiamare al suo posto un costruttore predefinito senza argomenti? Chiaramente il programmatore non stava cercando di chiamare un costruttore che ignorasse completamente il suo parametro, altrimenti non avrebbe specificato il parametro! –

risposta

2

Penso che sia un problema sia di leggibilità che di non assunzione di intenti. Tu dici

Java genera il costruttore vuoto predefinito; perché non può chiamarlo dietro le quinte in questo caso?

Eppure a me, sarebbe molto più senso per Java per chiamare implicitamente la Super(A) costruttore "dietro le quinte" che a chiamare il costruttore Super(), trascurando A.

E ce l'hai. Abbiamo già due ipotesi diverse su cosa dovrebbe (o potrebbe) accadere in questo caso.

Uno dei principi fondamentali del linguaggio Java è la trasparenza. Per quanto possibile, il programmatore dovrebbe essere in grado di vedere guardando al codice cosa accadrà, a volte a spese di convenienza o magia a livello di sintassi.

Un principio parallelo a quello non sta assumendo l'intento: nei casi in cui le intenzioni del programmatore sembrano ambigue, il linguaggio Java a volte favorirà un errore di compilazione anziché selezionare automaticamente un valore predefinito tramite un algoritmo di selezione (arbitrario o altrimenti).

+0

Buoni punti, ma sembra che tu abbia frainteso la mia affermazione. Lo aggiornerò per renderlo più chiaro. Potrebbe non cambiare il tuo argomento ... – arootbeer

2
public class Sub extends Super { } 

non ha il costruttore Sub (A a), ha solo il costruttore predefinito Sub().

I costruttori non sono ereditati.

+0

I costruttori sono ereditati ed è per questo che non funziona. – mikek3332002

+0

@ mikek3332002: Penso che possiamo solo invocare il costruttore super-classe dalla sub-classe ma non ereditato. –

0

Hai sovrascritto il costruttore pubblico predefinito, quindi non c'è nulla da chiamare.

Così la classe secondaria è equivalente a

public class Sub extends Super 
{ 
    protected Sub(){} 
    public Sub(A a) { } 
} 

Sarebbe perché la sua

  • comportamento Sane - si comporta come specificato dal programmatore, e segue anche il concetto di eredità in OOP lingue
  • Seguendo il suo retaggio C++
2

Le classi base necessitano di o chiamare i super-costruttori per assicurarsi che un oggetto sia istanziato correttamente. Per esempio considerare:

class Super { 
    final String field1; 

    public Super(String field1) { 
     this.field1 = field1; 
    } 
    ... 
} 


class Base extends Super { 
    final String field2; 

    public Base(String field2) { 
     this.field2 = field2; 
    } 
    ... 
} 

fa costruttore Base s' ignorare il costruttore Super? In tal caso, non è più possibile inizializzare field1, rendendo i metodi ereditati comportamenti inaspettati.

Nel momento in cui si aggiunge un costruttore non predefinito alla sottoclasse, i costruttori ereditati smettono di funzionare. Penso che sarebbe una caratteristica confusa e raramente utile che è stata aggiunta alla lingua, anche se tecnicamente non vedo ragioni per cui non sarebbe possibile.

+0

Il problema con questo contro-esempio è che non coinvolge i costruttori predefiniti. Hai ragione che questo codice è problematico; non sarebbe nemmeno compilato. Tuttavia, la mia domanda riguarda specificamente i costruttori predefiniti. – arootbeer

+0

L'esempio funziona perché Sub non ha costruttori e sarebbe una caratteristica di confusione se i costruttori venissero ereditati solo in quei casi. –

+0

Il mio esempio è problematico solo perché 'Sub' non ha costruttori. Sarò d'accordo con te sul fatto che sarebbe fonte di confusione, ma questa domanda è cercare di scoprire esattamente perché sarebbe fonte di confusione. – arootbeer

0

Sto mettendo questo fuori come una ragione tecnica per questo comportamento; Sono d'accordo con molte altre risposte che potrebbero semanticamente creare confusione.

consideri il seguente esempio:

public class A { } 

public abstract class Super { 
    protected Super() { 
     // perform time consuming, destructive, or 
     // otherwise one-time operations 
    } 

    public Super(A a) { 
     this(); 
     // perform A-related construction operations 
    } 
} 

public class Sub extends Super { } 

public class Consumer { 
    public Consumer() { 
     Sub sub = new Sub(new A()); 
    } 
} 

Quando il Sub è costruito, in questo caso il costruttore di default su Sub sarebbe chiamato, che a catena per il costruttore di default su Super (perché questa è la magia della lingua definisce). Quindi, la chiamata a Super(A) invocherà la logica che è stata progettata per essere eseguita una sola volta, in fase di costruzione. Questo ovviamente non è ciò che lo sviluppatore intende.

Anche senza la chiamata this() nel costruttore Super(A), l'intenzione dello sviluppatore non può essere determinata; i costruttori possono escludersi a vicenda per qualche motivo.

Problemi correlati