简体   繁体   中英

Referring to the defining class in generics

I tried to create a class the defines a tree, where the tree nodes can be tagged, and the tags are inherited by the descendant nodes, basically creating a taxonomy of things.

Then I had the idea, that sometimes tags shouldn't be inherited (exceptions like penguins can't fly), but I wanted to create a separate class for this, as there might be applications where the overhead of handling exceptions is wasted. So I created another class, NodeWithException. I wanted this tree to be of the same type of nodes, but have it as extensible as Node was.

My problem is with the line of

public class NodeWithException<TYPE_PARAM extends NodeWithException<?>> 

It will introduce raw types. But if I don't put "?" there, I would go down in a rabbit hole of infinite recursion. Is there a more elegant solution that avoids using raw types?

Code can be read below or downloaded from here: https://drive.google.com/file/d/0Bzgrcf36fidPWVlFdk9qdmdfT1E/view?usp=sharing

My classes:

Node.java

package tags_and_tax.model;

import java.util.HashSet;
import java.util.Set;

public class Node<TYPE_PARAM extends Node> {
    TYPE_PARAM parent = null;
    final Set<TYPE_PARAM> children = new HashSet<>();
    final Set<Tag> tags = new HashSet<>();

    public void addChild(TYPE_PARAM child){
        child.setParent(this);
    }

    public void  setParent(TYPE_PARAM newParent){
        if (parent != null){
            parent.children.remove(this);
        }
        parent = newParent;
        newParent.children.add(this);
    }
    public Set<Tag> getAllTags(){
        Set<Tag> result = new HashSet<>(tags);
        TYPE_PARAM node = parent;
        while(node != null){
            result.addAll(node.tags);
            node = (TYPE_PARAM) node.parent;
        }
        return result;
    }

    public Set<Tag> getTags(){
        return tags;
    }

}

NodeWithExceptions.java

package tags_and_tax.model;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;


@SuppressWarnings("rawtypes")
public class NodeWithException<TYPE_PARAM extends NodeWithException<?>> extends Node<TYPE_PARAM> {
    final Set<Tag> removeTags = new HashSet<>();

    public Set<Tag> getRemoveTags() {
        return removeTags;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Set<Tag> getAllTags(){
        Set<Tag> result = new HashSet<>(tags);
        LinkedList<TYPE_PARAM> parents = new LinkedList<>();
        parents.add((TYPE_PARAM) this);
        TYPE_PARAM node = parent;
        while(node != null){
            parents.add(node);
            node = (TYPE_PARAM) node.parent;
        }

        while(parents.size() > 0){
            node = parents.removeLast();
            result.addAll(node.tags);
            result.removeAll(node.removeTags);            
        }        
        return result;
    }    

}

Tag.java (this isn't that interesting, only here for the sake of completeness)

package tags_and_tax.model;

import java.util.HashMap;
import java.util.Map;

/**
 * Should be a "singletons" i.e. no two instances with the same name. Why not
 * use enums? Because they can't be created during runtime.
 * */
public class Tag {

    private final String name;

    private final int id;
    private static int idCounter = 0;

    static Map<String, Tag> map = new HashMap<>();

    private Tag(String name) {
        this.name = name;
        id = idCounter;
        idCounter++;
    }

    public String getName() {
        return name;
    }

    /** Only for debugging purposes (to see that things are really singletons.) */
    public int getId() {
        return id;
    }

    public static Tag getTag(Enum enum1) {
        return getTag(enum1.name());
    }

    public static Tag getTag(String str) {
        Tag result = map.get(str);
        if (result == null) {
            result = new Tag(str);
            map.put(str, result);
        }
        return result;
    }

    /** Only needed if deserialization messes things up. */
    @Override
    public boolean equals(Object tag2) {
        if (tag2 == null)
            return false;
        return name.equals(((Tag) tag2).name);
    }

    /* Generated by IDE */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public String toString() {
        return "name: " + name;
    }

}

I downloaded your code and I can't even compile Test1 class because of error: Error:(9, 27) java: type argument tags_and_tax.model.NodeWithException is not within bounds of type-variable TYPE_PARAM

As @immibis already answered you can use NodeWithException<T extends NodeWithException<T>> then you will get sth similar to Enum

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM