Sto costruendo una classe generica Tree<T>
, che supporta l'ereditarietà di sottoalberi. Ma ho riscontrato alcuni problemi. Per favore, per favore, aiutami?Costruisci un albero generico con ereditarietà
Descrizione
Definiamo la classe e la classe Tree
BlueTree
, dove BlueTree extends Tree
.
Definiamo la classe Leaf
e la classe RedLeaf
, dove RedLeaf extends Leaf
. Sono usati come "dati" che contengono gli alberi.
A Tree<Leaf>
indica un albero di tipo Tree
ei suoi "dati" sono di tipo Leaf
.
Per eredità (questo è non corretta eredità Java):
- un
Tree<Leaf>
può avere bambino di tipoTree<Leaf>
,Tree<RedLeaf>
,BlueTree<Leaf>
eBlueTree<RedLeaf>
.
.
- un
Tree<RedLeaf>
può avere bambino di tipoTree<RedLeaf>
, eBlueTree<RedLeaf>
,- ma non
Tree<Leaf>
oBlueTree<Leaf>
.
.
- un
BlueTree<Leaf>
può avere bambino di tipoBlueTree<Leaf>
, eBlueTree<RedLeaf>
,- ma non
Tree<Leaf>
oTree<RedLeaf>
.
.
- un
BlueTree<RedLeaf>
può avere bambino di tipoBlueTree<RedLeaf>
,- ma non
Tree<Leaf>
,Tree<RedLeaf>
oBlueTree<Leaf>
.
* Qui, "bambino" si intendono i rami/foglie dell'albero.
(un po 'complicato, è per questo che separare le linee.)
Il codice
(Se si dispone di una soluzione, potrebbe non essere necessario leggere l'illustrazione dettagliata dei miei tentativi di sotto . Se si desidera trovare la soluzione insieme, il mio codice può darvi alcune idee - o, li può confondere)
primo processo:. (la semplice)
012.// This is the focus of this question, the class signature
public class Tree<T> {
// some fields, but they are not important in this question
private Tree<? super T> mParent;
private T mData;
private ArrayList<Tree<? extends T>> mChildren;
// This is the focus of this question, the addChild() method signature
public void addChild(final Tree<? extends T> subTree) {
// add the subTree to mChildren
}
}
Questa struttura di classe soddisfa la maggior parte dei requisiti della descrizione. Tranne, permette
class BlueTree<T> extends Tree<T> { }
class Leaf { }
class RedLeaf extends Leaf { }
Tree<Leaf> tree_leaf = new Tree<Leaf>();
BlueTree<Leaf> blueTree_leaf = new BlueTree<Leaf>();
blueTree_leaf.addChild(tree_leaf); // should be forbidden
che viola
- un
BlueTree<Leaf>
non può avere bambino di tipoTree<Leaf>
.
Il problema è perché, in BlueTree<Leaf>
, il suo metodo firma addChild()
è ancora
public void addChild(final Tree<? extends Leaf> subTree) {
// add the subTree to mChildren
}
Il caso ideale è, la firma BlueTree<Leaf>.addChild()
metodo viene cambiato (automaticamente, su eredità) per
public void addChild(final BlueTree<? extends Leaf> subTree) {
// add the subTree to mChildren
}
(notare che questo metodo non può sovrascrivere il metodo precedente per ereditarietà, come la p tipi di arametro differiscono)
C'è una soluzione alternativa. Possiamo aggiungere un controllo di ereditarietà di classe, e gettare RuntimeException
per questo caso:
public void addChild(final Tree<? extends Leaf> subTree) {
if (this.getClass().isAssignableFrom(subTree.getClass()))
throw new RuntimeException("The parameter is of invalid class.");
// add the subTree to mChildren
}
Ma rendendolo un errore di compilazione è molto meglio di un errore di run-time. Mi piacerebbe applicare questo comportamento in fase di compilazione.
secondo processo
Il problema nella prima struttura di prova è, il tipo di parametro Tree
nel metodo addChild()
non è un parametro di tipo generico. Quindi non sarà aggiornato sull'ereditarietà. Questa volta, proviamo a renderlo anche un parametro di tipo generico.
In primo luogo, definire la classe generale Tree
.
public class Tree<T> {
private Tree<? super T> mParent;
private T mData;
private ArrayList<Tree<? extends T>> mChildren;
/*package*/ void addChild(final Tree<? extends T> subTree) {
// add the subTree to mChildren
}
}
Poi il TreeManager
che gestisce un oggetto Tree
.
public final class TreeManager<NodeType extends Tree<? super DataType>, DataType> {
private NodeType mTree;
public TreeManager(Class<NodeType> ClassNodeType) {
try {
mTree = ClassNodeType.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
public void managerAddChild(final NodeType subTree) {
mTree.addChild(subTree);
// compile error: The method addChild(Tree<? extends capture#1-of ? super DataType>)
// in the type Tree<capture#1-of ? super DataType>
// is not applicable for the arguments (NodeType)
}
// for testing
public static void main(String[] args) {
@SuppressWarnings("unchecked")
TreeManager<Tree <Leaf> , Leaf> tm_TreeLeaf_Leaf = new TreeManager<Tree <Leaf>, Leaf> ((Class<Tree <Leaf>>) new Tree <Leaf> ().getClass());
TreeManager<Tree <RedLeaf>, RedLeaf> tm_TreeRedLeaf_RedLeaf = new TreeManager<Tree <RedLeaf>, RedLeaf>((Class<Tree <RedLeaf>>) new Tree <RedLeaf>().getClass());
TreeManager<BlueTree<Leaf> , Leaf> tm_BlueTreeLeaf_Leaf = new TreeManager<BlueTree<Leaf>, Leaf> ((Class<BlueTree<Leaf>>) new BlueTree<Leaf> ().getClass());
TreeManager<BlueTree<RedLeaf>, RedLeaf> tm_BlueTreeRedLeaf_RedLeaf = new TreeManager<BlueTree<RedLeaf>, RedLeaf>((Class<BlueTree<RedLeaf>>) new BlueTree<RedLeaf>().getClass());
System.out.println(tm_TreeLeaf_Leaf .mTree.getClass()); // class Tree
System.out.println(tm_TreeRedLeaf_RedLeaf .mTree.getClass()); // class Tree
System.out.println(tm_BlueTreeLeaf_Leaf .mTree.getClass()); // class BlueTree
System.out.println(tm_BlueTreeRedLeaf_RedLeaf.mTree.getClass()); // class BlueTree
@SuppressWarnings("unchecked")
TreeManager<Tree <Leaf> , RedLeaf> tm_TreeLeaf_RedLeaf = new TreeManager<Tree <Leaf>, RedLeaf>((Class<Tree <Leaf>>) new Tree <Leaf> ().getClass());
TreeManager<BlueTree<Leaf> , RedLeaf> tm_BlueTreeLeaf_RedLeaf = new TreeManager<BlueTree<Leaf>, RedLeaf>((Class<BlueTree<Leaf>>) new BlueTree<Leaf> ().getClass());
System.out.println(tm_TreeLeaf_RedLeaf .mTree.getClass()); // class Tree
System.out.println(tm_BlueTreeLeaf_RedLeaf .mTree.getClass()); // class BlueTree
// the following two have compile errors, which is good and expected.
TreeManager<Tree <RedLeaf>, Leaf> tm_TreeRedLeaf_Leaf = new TreeManager<Tree <RedLeaf>, Leaf> ((Class<Tree <RedLeaf>>) new Tree <RedLeaf>().getClass());
TreeManager<BlueTree<RedLeaf>, Leaf> tm_BlueTreeRedLeaf_Leaf = new TreeManager<BlueTree<RedLeaf>, Leaf> ((Class<BlueTree<RedLeaf>>) new BlueTree<RedLeaf>().getClass());
}
}
Le TreeManager
inizializza senza problemi; le linee sono un po 'lunghe però. È conforme anche alle regole nella descrizione.
Tuttavia, è presente un errore di compilazione quando si chiama Tree.addChild()
all'interno di TreeManager
, come illustrato sopra.
terza prova
Per risolvere il compilazione errore nella seconda prova, ho provato a cambiare la firma della classe (a anche più a lungo). Ora mTree.addChild(subTree);
compila senza problemi.
// T is not used in the class. T is act as a reference in the signature only
public class TreeManager3<T, NodeType extends Tree<T>, DataType extends T> {
private NodeType mTree;
public TreeManager3(Class<NodeType> ClassNodeType) {
try {
mTree = ClassNodeType.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
public void managerAddChild(final NodeType subTree) {
mTree.addChild(subTree); // compile-error is gone
}
}
E l'ho provato con codice molto simile alla seconda prova. Crea senza problemi, come fa il secondo processo. (Basta anche di più.)
(Potete saltare il blocco di codice qui sotto, in quanto è solo logico ripetendo.)
public static void main(String[] args) {
@SuppressWarnings("unchecked")
TreeManager3<Leaf , Tree <Leaf> , Leaf> tm_TreeLeaf_Leaf = new TreeManager3<Leaf , Tree <Leaf>, Leaf> ((Class<Tree <Leaf>>) new Tree <Leaf> ().getClass());
TreeManager3<RedLeaf, Tree <RedLeaf>, RedLeaf> tm_TreeRedLeaf_RedLeaf = new TreeManager3<RedLeaf, Tree <RedLeaf>, RedLeaf>((Class<Tree <RedLeaf>>) new Tree <RedLeaf>().getClass());
TreeManager3<Leaf , BlueTree<Leaf> , Leaf> tm_BlueTreeLeaf_Leaf = new TreeManager3<Leaf , BlueTree<Leaf>, Leaf> ((Class<BlueTree<Leaf>>) new BlueTree<Leaf> ().getClass());
TreeManager3<RedLeaf, BlueTree<RedLeaf>, RedLeaf> tm_BlueTreeRedLeaf_RedLeaf = new TreeManager3<RedLeaf, BlueTree<RedLeaf>, RedLeaf>((Class<BlueTree<RedLeaf>>) new BlueTree<RedLeaf>().getClass());
System.out.println(tm_TreeLeaf_Leaf .mTree.getClass()); // class Tree
System.out.println(tm_TreeRedLeaf_RedLeaf .mTree.getClass()); // class Tree
System.out.println(tm_BlueTreeLeaf_Leaf .mTree.getClass()); // class BlueTree
System.out.println(tm_BlueTreeRedLeaf_RedLeaf.mTree.getClass()); // class BlueTree
@SuppressWarnings("unchecked")
TreeManager3<Leaf , Tree <Leaf> , RedLeaf> tm_TreeLeaf_RedLeaf = new TreeManager3<Leaf , Tree <Leaf>, RedLeaf>((Class<Tree <Leaf>>) new Tree <Leaf> ().getClass());
TreeManager3<Leaf , BlueTree<Leaf> , RedLeaf> tm_BlueTreeLeaf_RedLeaf = new TreeManager3<Leaf , BlueTree<Leaf>, RedLeaf>((Class<BlueTree<Leaf>>) new BlueTree<Leaf> ().getClass());
System.out.println(tm_TreeLeaf_RedLeaf .mTree.getClass()); // class Tree
System.out.println(tm_BlueTreeLeaf_RedLeaf .mTree.getClass()); // class BlueTree
// the following two have compile errors, which is good and expected.
TreeManager3<RedLeaf, Tree <RedLeaf>, Leaf> tm_TreeRedLeaf_Leaf = new TreeManager3<RedLeaf, Tree <RedLeaf>, Leaf> ((Class<Tree <RedLeaf>>) new Tree <RedLeaf>().getClass());
TreeManager3<RedLeaf, BlueTree<RedLeaf>, Leaf> tm_BlueTreeRedLeaf_Leaf = new TreeManager3<RedLeaf, BlueTree<RedLeaf>, Leaf> ((Class<BlueTree<RedLeaf>>) new BlueTree<RedLeaf>().getClass());
}
Tuttavia, sorge un problema quando provo a chiamare TreeManager3.managerAddChild()
.
tm_TreeLeaf_Leaf.managerAddChild(new Tree<Leaf>());
tm_TreeLeaf_Leaf.managerAddChild(new Tree<RedLeaf>()); // compile error: managerAddChild(Tree<RedLeaf>) cannot cast to managerAddChild(Tree<Leaf>)
tm_TreeLeaf_Leaf.managerAddChild(new BlueTree<Leaf>());
tm_TreeLeaf_Leaf.managerAddChild(new BlueTree<RedLeaf>()); // compile error: managerAddChild(BlueTree<RedLeaf>) cannot cast to managerAddChild(BlueTree<Leaf>)
Questo è comprensibile. TreeManager3.managerAddChild(NodeType)
significa TreeManager3.managerAddChild(Tree<T>)
e non è presente alcun jolly Tree<? extends T>
nel tipo di parametro, ad esempio Tree.addChild(final Tree<? extends T> subTree)
nella prima prova.
Supplicare per il vostro aiuto ...
ho già a corto di idee. Stavo andando nella direzione sbagliata per risolvere questo problema? Ho passato molto tempo a scrivere questa domanda e ho provato il mio massimo sforzo per renderlo più leggibile, più facile da capire e da seguire. Devo dire scusa che è ancora molto lungo e prolisso. Ma potresti aiutarmi se conosci la strada, o per favore dammi qualche idea che hai? Ogni tuo input è molto apprezzato. Molte grazie!
Modifica # 1 (per la comment below)
sede nel primo processo, consentire solo mChildren
essere modificato da addChild()
(e altri metodi con l'assegno isAssignableFrom()
), quindi, anche permettendo l'eredità utente di Tree
e l'override di addChild()
non interromperà l'integrità dell'albero.
/developer/util/Tree.java
package developer.util;
import java.util.ArrayList;
public class Tree<T> {
private Tree<? super T> mParent;
private final ArrayList<Tree<? extends T>> mChildren = new ArrayList<Tree<? extends T>>();
public int getChildCount() { return mChildren.size(); }
public Tree<? extends T> getLastChild() { return mChildren.get(getChildCount()-1); }
public void addChild(final Tree<? extends T> subTree) {
if (this.getClass().isAssignableFrom(subTree.getClass()) == false)
throw new RuntimeException("The child (subTree) must be a sub-class of this Tree.");
subTree.mParent = this;
mChildren.add(subTree);
}
}
/user/pkg/BinaryTree.java
package user.pkg;
import developer.util.Tree;
public class BinaryTree<T> extends Tree<T> {
@Override
public void addChild(final Tree<? extends T> subTree) {
if (getChildCount() < 2) {
super.addChild(subTree);
}
}
}
/Main.java
import user.pkg.BinaryTree;
import developer.util.Tree;
public class Main {
public static void main(String[] args) {
Tree<Integer> treeOfInt = new Tree<Integer>();
BinaryTree<Integer> btreeOfInt = new BinaryTree<Integer>();
treeOfInt.addChild(btreeOfInt);
System.out.println(treeOfInt.getLastChild().getClass());
// class user.pkg.BinaryTree
try {
btreeOfInt.addChild(treeOfInt);
} catch (Exception e) {
System.out.println(e);
// java.lang.RuntimeException: The child (subTree) must be a sub-class of this Tree.
}
System.out.println("done.");
}
}
Cosa ne pensi?
hai considerato il tuo oggetto come Albero, FOGLIA>? quindi in questo caso avrai anche tu il tuo albero –
user902383
Grazie per la tua risposta !! Devo provarlo una volta tornato a casa. Se funziona, sarebbe fantastico! E il modo semplice, bello e pulito :-) – midnite
@ user902383, Grazie ancora. ho appena provato nella mia eclissi. 'Albero di classe pubblica, T> {}' non viene compilato, purtroppo. –
midnite