2012-12-19 16 views
5

Mam aplikację, która wykonuje różne algorytmy analizy na wykresach węzłów i krawędzi G (N, E). Atrybuty węzłów i krawędzi różnią się w zależności od aplikacji i tworzą hierarchię dziedziczenia w oparciu o typ wykresu i charakter atrybutów. Na przykład katalog główny hierarchii węzłów może reprezentować najbardziej ogólne niekierowane wykresy cykliczne (NcgNode). Podklasa NcgNode może reprezentować ukierunkowane wykresy cykliczne (DcgNode), a następnie DagNode itp. Algorytmy, które można zastosować do DAG, różnią się od algorytmów NCG, ale nie odwrotnie. Kluczowym zachowaniem katalogu głównego drzewa jest dodawanie i pobieranie sąsiednich węzłów na wykresie. Pytanie brzmi, jak to zrobić bez tworzenia wyjątku "niezaznaczonego"?Dziedziczenie i generics

lakoniczny wersja kodu może wyglądać następująco:

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. 
} 

Czy istnieje lepszy sposób projektowania jest?

Odpowiedz

0

zmodyfikować swoją metodę jako takie poniżej:

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

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

To rozwiązanie nie pozwala rozmówcy na użycie specyfiki konkretnej podklasy, którą zwraca, bez wykonywania rzutu niebezpiecznego. Kopanie puszki w dół drogi. –

0

Check out "typy własny ograniczony". (EDIT: nie wiem, rozumiem tu dół głosów)

Twoja klasa korzeń powinien być abstrakcyjne i rzeczywisty typ węzła N powinna być parametrem typu do klasy, jak w

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); 
    } 
} 

betonowe podklasy może następnie podaj swoje typy jako N. Dla hierarchii głębokiego dziedziczenia utrzymuj przy życiu "Mój typ" inną klasą abstrakcyjną.

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) "self-bounded types" nie działają w Javie. 2) jeśli zastąpiony 'AbstractNode >' z 'AbstractNode ' i 'AbstractDcgNode >' z 'AbstractDcgNode ' to działa w ten sam sposób – newacct

+0

można być bardziej szczegółowe na temat Komentarz 1) ?Nie można egzekwować dokładnie tego, że parametr typu ogranicza klasę deklarującą, ale jest bliższą niż komentarz 2) sugestię, która pozwala na ściśle więcej programów do kompilacji niż moja - i więcej niż szukał OP. –

1

Wystarczy dokonać wykazy mają typ NcgNode np

private List<NcgNode> nodeListSrc_ = null; 

Nadal można umieścić podklasy NcgNode na tych listach.

1

Powinieneś zrobić to jak poniżej. Mieć metody zdefiniowane w klasie abstrakcyjnej (NcgNode), sparametryzowane na typach potomnych. Tak więc, addNode i getNode można łatwo zapisać. Wtedy będziesz miał konkretne implementacje (użyłem DcgNode i DagNode, nie jestem pewien, czy to jest to, czego chcesz) być podklasą tego, sparametryzowane na sobie. Pozwala to na późniejsze (patrz poniżej) algorytmy, które wymagają, aby dzieci węzła były tego samego typu co węzeł.

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) { } 

Twój pomysł DagNode jest podklasą DcgNode nie może być bezpieczne, ponieważ jeśli DagNode „is-a” DcgNode, to oznacza, że ​​można umieścić dowolny DcgNode do niego jako jego dziecko, które nie jest to, czego chcieć.

+0

Co jeśli chcesz również przedłużyć DcgNode lub DagNode? – Sarevok

+0

Jeśli chcę zapisać listę , która może przechowywać zarówno węzły DcgNode, jak i DagNode, czy powinienem ją zadeklarować? Kompilator wyświetla ostrzeżenie, jeśli zadeklaruję to w ten sposób, ponieważ używam typu surowego. – Sarevok