2009-03-19 10 views
10

In un programma Java, ho più sottoclassi che ereditano da un genitore (che è astratto). Volevo esprimere che ogni bambino dovrebbe avere un membro impostato una sola volta (cosa che stavo pensando di fare dal costruttore). Il mio piano era di codificare s.th. In questo modo:In Java, perché non posso dichiarare un membro finale (senza inizializzarlo) nella classe genitore e impostare il suo valore nella sottoclasse? Come posso lavorare intorno?

public abstract class Parent { 
    protected final String birthmark; 
} 

public class Child extends Parent { 
    public Child(String s) { 
     this.birthmark = s; 
    } 
} 

Tuttavia, questo sembra non piacere agli dei Java. Nella classe genitore, ricevo il messaggio che "birthmark" potrebbe non essere stato inizializzato ", nella classe figlia ottengo" Non è possibile accedere al campo finale birthmark ".

Quindi qual è il modo Java per questo? Cosa mi manca?

+0

Non una risposta diretta, ma nel formato file di classe i campi dell'istanza finale possono essere impostati solo dallo stesso file di classe ma non necessariamente nel costruttore. IIRC. –

+0

Domanda correlata: http://stackoverflow.com/questions/14383276/initialize-member-of-abstract-class-without-subclasses-having-write-access – user905686

risposta

19

Non puoi farlo perché mentre confrontando la classe padre, il compilatore non può essere sicuri che la sottoclasse si inizializza. Dovrete inizializzare nel costruttore del genitore, e avere il bambino chiamata al costruttore del genitore:

public abstract class Parent { 
    protected final String birthmark; 
    protected Parent(String s) { 
     birthmark = s; 
    } 
} 

public class Child extends Parent { 
    public Child(String s) { 
     super(s); 
     ... 
    } 
} 
+3

+1 per rendere protetto il genitore dei genitori! –

+0

Se dovessi saltare dichiarando il costruttore del bambino (e, in questo caso, probabilmente mantenere pubblico il genitore del genitore), potrei fare "nuovo figlio" ("imposta questo") "e ottenere quello che voglio, giusto? –

7

passarlo al costruttore genitore:

public abstract class Parent { 
    private final String birthmark; 
    public Parent(String s) { 
     birthmark = s; 
    } 
} 

public class Child extends Parent { 
    public Child(String s) { 
     super(s); 
    } 
} 
+0

Nel mio caso, non posso passare come l'oggetto richiede un riferimento a 'questo', qualche consiglio? –

2

Un altro modo Java-ish per fare questo è probabilmente di avere la classe padre per definire un abstract "getter" e farli implementare dai bambini. Non è un ottimo modo per farlo in questo caso, ma in alcuni casi può essere esattamente quello che vuoi.

+0

chiamare il metodo getter astratto nel costruttore può essere potenzialmente pericoloso, tuttavia, una buona regola è di non chiamare mai un metodo sovrascrivibile, directlry o indiretto, da un costruttore. – TofuBeer

+0

@TofuBeer qualsiasi esempio di quanto pericoloso possa essere? questa risposta funzionerà perfettamente per me [qui] (http://stackoverflow.com/questions/34822434/composite-inheritance-how-to-assign-a-final-field-at-sub-class-constructor-whic)! EDIT [mmm] (http://stackoverflow.com/questions/3404301/whats-wrong-with-overridable-method-calls-in-constructors) –

+0

Se si chiama un metodo sottoposto a override in un costruttore, e quel metodo accede a una variabile dalla classe figlia, allora quella variabile non sarà ancora inizializzata perché il costruttore della classe figlia non è ancora stato istanziato. È pericoloso perché potrebbe funzionare ora, ma se qualcuno in seguito cambierà il getter per farlo si bloccherà o otterrà il valore sbagliato. – TofuBeer

0

Sì, i membri finali devono essere assegnati nella classe in cui sono dichiarati. È necessario aggiungere un costruttore con un argomento String a Parent.

0

Dichiarare un costruttore nella superclasse chiamata dalla sottoclasse. È necessario impostare il campo nella superclasse per assicurarsi che sia inizializzato, altrimenti il ​​compilatore non può essere sicuro che il campo sia inizializzato.

0

Probabilmente si desidera avere un costruttore Parent (String birthmark) in modo da poter garantire nella classe Parent che final sia sempre inizializzato. Quindi puoi chiamare super (birthmark) dal tuo costruttore Child().

1

Perché non delegare l'inizializzazione a un metodo. Quindi sovrascrivere il metodo nella classe genitore.

public class Parent { 
    public final Object x = getValueOfX(); 
    public Object getValueOfX() { 
     return y; 
    } 
} 
public class Child { 
    @Override 
    public Object getValueOfX() { 
    // whatever ... 
    } 
} 

Ciò dovrebbe consentire l'inizializzazione personalizzata.

+1

Questo idioma è una pessima idea ... se un utente fa getValueofX() dipende da qualsiasi campo della classe Child, ci sarà una NullPointerException, dal momento che Child non è ancora stato costruito. Può funzionare se si sta molto attenti con i documenti, ma gli utenti stupidi/maliziosi possono romperli in un batter d'occhio. –

+0

@Chamelaeon: questo è un commento così stupido, che cosa farebbe un utente malintenzionato o stupido a scrivere codice per il tuo progetto? Un utente malintenzionato o stupido potrebbe scrivere System.exit (0)? –

+0

questo aiuterà [qui] (http://stackoverflow.com/questions/34822434/composite-inheritance-how-to-assign-a-final-field-at-sub-class-constructor-whic), thx! –

2

farei così:

public abstract class Parent 
{ 
    protected final String birthmark; 

    protected Parent(final String mark) 
    { 
     // only if this makes sense. 
     if(mark == null) 
     { 
      throw new IllegalArgumentException("mark cannot be null"); 
     } 

     birthmark = mark; 
    } 
} 

public class Child 
    extends Parent 
{ 
    public Child(final String s) 
    { 
     super(s); 
    } 
} 

finale significa che la variabile può essere inizializzata una volta per esempio. Il compilatore non è in grado di assicurarsi che ogni sottoclasse fornisca l'assegnazione alla voglia, in modo che imponga l'assegnazione nel costruttore della classe genitore.

Ho aggiunto il controllo per null solo per mostrare che si ottiene anche il vantaggio di poter controllare gli argomenti in un posto piuttosto che ogni cosntructor.

+0

C'è un motivo per contrassegnare anche i parametri nella dichiarazione della funzione finale? Il compilatore mi lascia andare via senza. –

+0

Lo faccio per abitudine ... ti impedisce di fare param = instanceVariable. Se l'hotspot è sempre in grado di ottimizzare meglio i vars finali (non credo che lo faccia ora) allora anche il mio codice sarà magicamente più veloce :-) – TofuBeer

Problemi correlati