2012-12-19 17 views
5

Ho un'applicazione che esegue vari algoritmi di analisi su grafici di nodi e bordi G (N, E). Gli attributi di nodi e spigoli variano con l'applicazione e formano una gerarchia di ereditarietà in base al tipo di grafico e alla natura degli attributi. Ad esempio, la radice della gerarchia del nodo potrebbe rappresentare i più comuni grafici ciclici non indirizzati (NcgNode). Una sottoclasse di NcgNode potrebbe rappresentare grafici ciclici diretti (DcgNode), seguiti da DagNode, ecc. Gli algoritmi che possono essere applicati ai DAG sono diversi da quelli degli NCG, ma non viceversa. Un comportamento chiave della radice dell'albero è quello di aggiungere e recuperare nodi adiacenti del grafico. La domanda è come farlo senza creare un'eccezione "non controllata"?Ereditarietà e generici

Una versione concisa del codice potrebbe assomigliare a questo:

import java.util.ArrayList; 
import java.util.List; 

public class NcgNode { 
    private List<NcgNode> nodeList_ = null; 
    private List<? extends NcgNode> nodeListSrc_ = null; 
    private List<? super NcgNode> nodeListSink_ = null; 

    public <N extends NcgNode> void addNode(N node) { 
     if (nodeList_ == null) { 
      nodeList_ = new ArrayList<NcgNode>(); 
      nodeListSrc_ = nodeList_; 
      nodeListSink_ = nodeList_; 
     } 
     nodeListSink_.add(node); 
    } 

    @SuppressWarnings("unchecked") 
    // Any way to avoid this? 
    public <N extends NcgNode> N getNode(int n) { 
     if ((nodeList_ == null) || (n >= nodeList_.size())) 
      return null; 
     // causes unchecked warning: 
     return (N) nodeListSrc_.get(n); 
    } 
} 

class DcgNode extends NcgNode { 
    // enables DCG algorithms, etc 
} 

class DagNode extends DcgNode { 
    // enables DAG algorithms, etc. 
} 

C'è un modo migliore per progettare questo?

risposta

0

modificare il metodo come tale seguito:

public NcgNode getNode(int n) { 
    if ((nodeList_ == null) || (n >= nodeList_.size())) { 
    return null; 
} 

return (NcgNode) nodeListSrc_.get(n); 
} 
+3

Questa soluzione non consente al chiamante di utilizzare le specifiche della sottoclasse concreta che restituisce senza eseguire un cast non sicuro. Calcia la lattina in fondo alla strada. –

0

Partenza "tipi di auto-limitata". (EDIT: Non sono sicuro di capire i down-voti qui)

La classe root dovrebbe essere astratto e il tipo di nodo reale N dovrebbe essere un parametro di tipo alla classe, come in

public abstract class AbstractNode< N extends AbstractNode<N> > { 
    private List<N> nodeList_ = null; 

    public synchronized void addNode(N node) { 
     if (nodeList_ == null) 
      nodeList_ = new ArrayList<N>(); 
     nodeList_.add(node); 
    } 

    public N getNode(int n) { 
     if (nodeList_ == null || n >= nodeList_.size()) 
      throw new NoSuchElementException(); 
     return nodeList_.get(n); 
    } 
} 

sottoclassi calcestruzzo può quindi fornire i propri tipi come N. Per le gerarchie di ereditarietà profonda, mantenere il "My Type" vivo con un'altra classe astratta.

class NcgNode extends AbstractNode<NcgNode> { 
} 

abstract class AbstractDcgNode< N extends AbstractDcgNode<N> > extends AbstractNode<N> { 
    // enables DCG algorithms, etc 
} 

class DcgNode extends AbstractDcgNode<DcgNode> { 
} 

class DagNode extends AbstractDcgNode<DagNode> { 
    // enables DAG algorithms, etc 
} 
+0

1) I "tipi auto-limitati" non funzionano in Java. 2) se è stato sostituito 'AbstractNode >' con 'AbstractNode ' e 'AbstractDcgNode >' con 'AbstractDcgNode ' che avrebbe funzionato allo stesso modo – newacct

+0

Puoi essere più preciso circa commento 1) ?Non si può far rispettare esattamente il fatto che un parametro di tipo limita la classe che lo dichiara, ma è più vicino del commento 2) suggerimento che permette a più programmi di compilare più di quanto faccia il mio - e più di quanto l'OP stesse cercando. –

1

Basta fare le vostre liste hanno tipo NcgNode, ad esempio

private List<NcgNode> nodeListSrc_ = null; 

È ancora possibile mettere sottoclassi di NcgNode in queste liste.

1

Si dovrebbe fare qualcosa di simile al seguente. Hanno i metodi definiti in una classe astratta (NcgNode), parametrizzati sul tipo di bambini. Pertanto, addNode e getNode possono essere scritti facilmente. Quindi avrai implementazioni specifiche (ho usato DcgNode e DagNode, non sono sicuro se questo è quello che vuoi) essere una sottoclasse di questo, parametrizzato su se stesso. Ciò consente di avere algoritmi successivi (vedi sotto) che richiedono che i figli di un nodo siano dello stesso tipo del nodo.

public abstract class NcgNode<N> { 
    private List<N> nodeList_ = null; 

    public void addNode(N node) { 
     if (nodeList_ == null) { 
      nodeList_ = new ArrayList<N>(); 
     } 
     nodeList_.add(node); 
    } 

    // Any way to avoid this? 
    public N getNode(int n) { 
     if ((nodeList_ == null) || (n >= nodeList_.size())) 
      return null; 
     return nodeList_.get(n); 
    } 
} 

class DcgNode extends NcgNode<DcgNode> { 
    // enables DCG algorithms, etc 
} 

class DagNode extends NcgNode<DagNode> { 
    // enables DAG algorithms, etc. 
} 

//... 
static <N extends NcgNode<N>> void someAlgorithm(N node) { } 

La tua idea di DagNode essendo una sottoclasse di DcgNode non può essere sicuro, perché se un DagNode "is-a" DcgNode, allora significa che si può inserire qualsiasi DcgNode in esso come il suo bambino, che non è quello che si volere.

+0

Cosa fare se si desidera estendere DcgNode o DagNode? – Sarevok

+0

Se voglio memorizzare un elenco in grado di memorizzare sia DcgNode che DagNode, dovrei dichiararlo? Il compilatore mostra un avvertimento se lo dichiaro in quel modo dato che sto usando il tipo raw. – Sarevok