Java通用类模板

时间:2012-12-31 21:45:33

标签: java generics

尝试使用具有不同类型节点对象的多个节点创建N-ary树[Country | State etc],我尝试从 -

修改下面的泛型类

https://github.com/vivin/GenericTree/blob/master/src/main/java/net/vivin/GenericTreeNode.java

我尝试了以下内容 -

package com.mycompany.ds;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GenericTreeNode<T>{

    private T data;
    private List<GenericTreeNode<? super T>> children;
    private GenericTreeNode<? super T> parent;

    public GenericTreeNode() {
        super();
        children = new ArrayList<GenericTreeNode<? super T>>();
    }

    public GenericTreeNode(T data) {
        this();
        setData(data);
    }

    public GenericTreeNode<? super T> getParent() {
        return this.parent;
    }

    public List<GenericTreeNode<? super T>> getChildren() {
        return this.children;
    }

    public int getNumberOfChildren() {
        return getChildren().size();
    }

    public boolean hasChildren() {
        return (getNumberOfChildren() > 0);
    }

    public void setChildren(List<GenericTreeNode<? super T>> children) {
        for(GenericTreeNode<? super T> child : children) {
           child.parent = this;
        }

        this.children = children;
    }

    public void addChild(GenericTreeNode<? super T> child) {
        child.parent = this;
        children.add(child);
    }

    public void addChildAt(int index, GenericTreeNode<T> child) throws IndexOutOfBoundsException {
        child.parent = this;
        children.add(index, child);
    }

    public void removeChildren() {
        this.children = new ArrayList<GenericTreeNode<? super T>>();
    }

    public void removeChildAt(int index) throws IndexOutOfBoundsException {
        children.remove(index);
    }

    public GenericTreeNode<? super T> getChildAt(int index) throws IndexOutOfBoundsException {
        return children.get(index);
    }

    public T getData() {
        return this.data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String toString() {
        return getData().toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
           return true;
        }
        if (obj == null) {
           return false;
        }
        if (getClass() != obj.getClass()) {
           return false;
        }
        GenericTreeNode<?> other = (GenericTreeNode<?>) obj;
        if (data == null) {
           if (other.data != null) {
              return false;
           }
        } else if (!data.equals(other.data)) {
           return false;
        }
        return true;
    }

    /* (non-Javadoc)
    * @see java.lang.Object#hashCode()
    */
    @Override
    public int hashCode() {
       final int prime = 31;
       int result = 1;
       result = prime * result + ((data == null) ? 0 : data.hashCode());
       return result;
    }

    public String toStringVerbose() {
        String stringRepresentation = getData().toString() + ":[";

        for (GenericTreeNode<? super T> node : getChildren()) {
            stringRepresentation += node.getData().toString() + ", ";
        }

        //Pattern.DOTALL causes ^ and $ to match. Otherwise it won't. It's retarded.
        Pattern pattern = Pattern.compile(", $", Pattern.DOTALL);
        Matcher matcher = pattern.matcher(stringRepresentation);

        stringRepresentation = matcher.replaceFirst("");
        stringRepresentation += "]";

        return stringRepresentation;
    }
}

但是以下方法中的错误 -

 public void setChildren(List<GenericTreeNode<? super T>> children) {
        for(GenericTreeNode<? super T> child : children) {
           child.parent = this;
        }

        this.children = children;
    }

    public void addChild(GenericTreeNode<? super T> child) {
        child.parent = this;
        children.add(child);
    }

错误 -

1 - Type mismatch: cannot convert from GenericTreeNode<T> to GenericTreeNode<? super capture#2-of ? super 
 T>

2 - Type mismatch: cannot convert from GenericTreeNode<T> to GenericTreeNode<? super capture#4-of ? super 
 T>

我该如何解决这些问题?

3 个答案:

答案 0 :(得分:0)

您可以创建表示GISEntity的类/接口,并创建泛型类型T扩展GISEntity的通用树节点。这将允许您拥有不同类型的GISEntity子类的节点 - 国家/州等。

答案 1 :(得分:0)

建立ditkin的答案: 在使所有类实现或扩展GISEntity之后,您将以这种方式编写树:

public class GenericTreeNode<T extends GISEntity>{

    private T data;
    private List<GenericTreeNode<? extends GISEntity>> children;
    private GenericTreeNode<? extends GISEntity> parent;

    public GenericTreeNode() {
        super();
        children = new ArrayList<GenericTreeNode<? extends GISEntity>>();
    }

    ////////
    ......
    ////////

    public void addChild(GenericTreeNode<? extends GISEntity> child) {
        child.parent = this;
        children.add(child);
    }

    public void addChildAt(int index, GenericTreeNode<? extends GISEntity> child) throws IndexOutOfBoundsException {
        child.parent = this;
        children.add(index, child);
    }

    ////////
    ......
    ////////

}

请注意,它不会真正帮助您避免类转换。问题是,只要您将子节点添加到节点,当您检索它们时,您就会知道它们是GISEntity,因为类型擦除。所以这种技术只能给你一点安全性。

答案 2 :(得分:0)

使用Generic以便在同一个集合中存储不同类型的对象并不是一个好主意。您应该做的是创建一个层次结构并使用它来存储您的对象。通过良好的设计,基类将具有访问不同对象而无需进行渲染所需的所有功能;否则你将不得不在这里和那里写一些演员。这是一个代码示例(请注意,这里的设计远非最佳,只是为了展示虚函数和多态的使用):

static class GISEntity {
    final String name;
    public GISEntity (String name) { this.name = name; }
    public String getName() { return name; }
    public String getTypeName() { return "GISEntity"; }
    public String toString() { return name; }
}
//
static class Country extends GISEntity {
    final String typeName = "country";
    public Country (String name) { super(name); }
    public String getTypeName() { return typeName; }
    public String toString() { return name; }
}
//
static class State extends GISEntity {
    public State (String name) { super(name); }
    public String getTypeName() { return "state"; }
    public String toString() { return name; }
}
//
static class Territory extends GISEntity {
    public Territory (String name) { super(name); }
    public String getTypeName() { return "territory"; }
    public String toString() { return name; }
}
//
// Here's an example of subclassing GenericTreeNode<GISEntity>:
//
static class IsATerritory extends GenericTreeNode<GISEntity> {

    IsATerritory (String name) { super (new Territory (name)); }

    public GISEntity getData() {
        State s = new State (super.getData().getName().toUpperCase());
        return s; }
};
//
// Here we put some data. Note that the order of insertion is important
// for the tree and that it's not alphabetical in this example.
//
GenericTree<GISEntity> earth = new GenericTree<GISEntity>() ;
//
GenericTreeNode<GISEntity> ListOfCountries = new GenericTreeNode<GISEntity>(new GISEntity("List of countries"));
//
GenericTreeNode<GISEntity> US = new GenericTreeNode<GISEntity>(new Country("United States"));
GenericTreeNode<GISEntity> Washington = new GenericTreeNode<GISEntity>(new State("Washington"));
GenericTreeNode<GISEntity> Florida = new GenericTreeNode<GISEntity>(new State("Florida"));
//
GenericTreeNode<GISEntity> Canada = new GenericTreeNode<GISEntity>(new Country("Canada"));
//
// We are now using some different ways for creating the nodes:
//
@SuppressWarnings("unchecked")
List<GenericTreeNode<GISEntity>> CanadaProvinces = new ArrayList<GenericTreeNode<GISEntity>>(
Arrays.asList(new GenericTreeNode<GISEntity>(new State("Quebec")), 
    new GenericTreeNode<GISEntity>(new State("Ontario")))
);
//
US.addChild(Washington);
US.addChild(Florida);
//
// Here's are two examples of subclassing; this time with anonymous classes.
// Don't forget that these two anonymous classes will hold an hidden reference
// to the outer classe as they are not static!
//
GenericTreeNode<GISEntity> alberta = new GenericTreeNode<GISEntity>() {
    { setData(new State ("Alberta")); }

    public GISEntity getData() {
        State s = new State (super.getData().getName().toUpperCase());
        return s; 
      }
};
//
GenericTreeNode<GISEntity> saskatchewan = new GenericTreeNode<GISEntity>(new State ("saskatchewan")) {
    public GISEntity getData() {
        State s = new State (super.getData().getName().toUpperCase());
        return s; }
};
//
CanadaProvinces.add(alberta);
CanadaProvinces.add(saskatchewan);
//
// Other ways for creating the nodes:
CanadaProvinces.add(new GenericTreeNode<GISEntity>(new State("Manitoba")));
//
// Note the use of the IsATerritory subclass:
CanadaProvinces.add(new IsATerritory("Northwest Territories"));
//
Canada.setChildren(CanadaProvinces);
//
ListOfCountries.addChild(Canada);
ListOfCountries.addChild(US);
//
earth.setRoot(ListOfCountries);
//
System.out.println(earth.toString());
System.out.println();
System.out.println(earth.toStringWithDepth());
System.out.println();
System.out.println(ListOfCountries.toStringVerbose());
//
List<GenericTreeNode<GISEntity>> loc = earth.build(GenericTreeTraversalOrderEnum.PRE_ORDER);
System.out.println(loc);
//
Map<GenericTreeNode<GISEntity>, Integer> locd = earth.buildWithDepth(GenericTreeTraversalOrderEnum.PRE_ORDER);
System.out.println(locd);
//
Map<GenericTreeNode<GISEntity>, Integer> locd2 = earth.buildWithDepth(GenericTreeTraversalOrderEnum.POST_ORDER);
System.out.println(locd2);
//
// Two examples of iteration; showing both the use of the instanceof operator
// and of virtual (or override) functions:
// 
for (GenericTreeNode<GISEntity> gen: loc) {
    GISEntity data = gen.getData();

    if (data instanceof State) {
        System.out.println("Is State: " + data.getName());
    } else if (data instanceof Country) {
        System.out.println("Is Country: " + data.getName());
    } else {
        System.out.println(data.getTypeName() + data.getName());
    }
}
//
for (Entry<GenericTreeNode<GISEntity>, Integer> entry: locd.entrySet()) {
    GISEntity data = entry.getKey().getData();
    Integer depth = entry.getValue();

    if (data instanceof State) {
        System.out.println(depth.toString() + ": Is State: " + data.getName());
    } else if (data instanceof Country) {
        System.out.println(depth.toString() + ": Is Country: " + data.getName());
    } else {
        System.out.println(depth.toString() + ": " + data.getTypeName() + data.getName());
    }
}

在这个例子中,我以三种不同的方式(两个匿名类,一个命名类)子类化了GenericTreeNode类,以便更改getData,以便它返回一个新的GISEntity,其名称已被其UpperCase替换副本。

请注意,所有这三个子类,我使用GenericTreeNode<GISEntity>而不是GenericTreeNode<Territory>。这是因为即使TerritoryGISEntry的子类,类GenericTreeNode<Territory>也不是GenericTreeNode<GISEntry>的子类。

对于使用GenericTreeNode<Territory>GenericTreeNode<GISEntry>混合的内容,我们必须使用? extends GISEntry? super GISEntry,这将使通用的复杂性增加一千码。除非你想对泛型类GenericTree<>GenericTreeNode<>做一些重要的子类化,否则使用?类型是完全没用的。即使是收集不同类型的物品。除非您在通用代码方面拥有多年经验,否则请勿使用?表示法。使用更简单的通用代码,大多数项目都会完全正常。

我还为感兴趣的人添加了build()buildWithDepth()函数的通用树迭代的一些示例。

最后,作为参考,这个通用树在http://vivin.net/2010/01/30/generic-n-ary-tree-in-java/(3页)中解释。