TreeItem中的通配符和泛型

时间:2017-06-09 12:31:30

标签: java javafx

我有以下课程:

class TreeItem<T extends TreeItem<?>>{

    private final ObservableList<T> childs;
    private T parent;

    public void addChild(T unit){
        childs.add(unit);
        unit.setParent(this);
    }

    public <T> void setParent(T parent){
        this.parent = parent;
    }
}

我在setParent上收到此消息:

  

不兼容的类型。

     

必填: T

     

发现: T

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:4)

Rewrite this:

public <T> void setParent(T parent){
    this.parent = parent;
}

With this:

public void setParent(T parent){
    this.parent = parent;
}

Also I would suggest to remove the wildcare in the class name declaration, as the code would not compile.

So replace this:

class TreeItem<T extends TreeItem<?>>

With this:

class TreeItem<T extends TreeItem>

You have a final variable in your class. So it should be initialized either in constructor or inline. As long as you use generic variable and generic type is resolved when you instantiate a new object, the right way to do is to initialize it in constructor like this:

public TreeItem(ObservableList<T> childs) {
    this.childs = childs;
}

When you are done with the above proposed changes you may notice that the compiler warns you with the message: Unchecked call to 'setParent(T)'. That means that the compiler does not guarantee the code is safe during runtime and possible heap pollution may occure. I will illustrate it with an example. The following code while running ends up with ClassCastException as we set parent variable with the type which is not T (it is possible due to type erasure).

class ChildTreeItem<T extends TreeItem> extends TreeItem<T> {
    public ChildTreeItem(ObservableList childs) {
        super(childs);
    }
}



public class TreeItem<T extends TreeItem>{

    private final ObservableList<T> childs;
    private T parent;

    public TreeItem(ObservableList<T> childs) {
        this.childs = childs;
    }

    public void addChild(T unit){
        childs.add(unit);
        unit.setParent(this);
    }

    public void setParent(T parent){
        this.parent = parent;
    }

    public T getParent() {
        return parent;
    }

    public static void main(String[] args) {
        ChildTreeItem<ChildTreeItem> treeItem =
            new ChildTreeItem<>(new ObservableSequentialListWrapper<>(new ArrayList<>()));
        TreeItem<ChildTreeItem> parentItem =
            new TreeItem<>(new ObservableSequentialListWrapper<>(new ArrayList<>()));

        parentItem.addChild(treeItem);
        List<ChildTreeItem> itemList = new ArrayList<>();
        itemList.add(treeItem.getParent()); //<------------------- Heap pollution

        ChildTreeItem childTreeItem = itemList.get(0); //<-------- ClassCastException
    }
}

The possible solution to this problem is to not parametrize the variable parent but to make it TreeItem type:

public class TreeItem<T extends TreeItem>{

    private final ObservableList<T> childs;
    private TreeItem parent;

    public TreeItem(ObservableList<T> childs) {
        this.childs = childs;
    }

    public void addChild(T unit){
        childs.add(unit);
        unit.setParent(this);
    }

    public void setParent(TreeItem parent){
        this.parent = parent;
    }

    public TreeItem getParent() {
        return parent;
    }
}

Hope this helps.

答案 1 :(得分:0)

Since you have define already in your class level of Generic type. It is not clear why you put before your setParent function. But let's assume you want to define a generic type in function level, then when you call it, you need to specify the type

Ex:

TreeItem<String> treeItem = new TreeItem<String>();
treeItem.<String>setParent("something");