简体   繁体   中英

Recursive generics in Java

I am trying to implement a class representing an XML tree as follows:

    public class XML<T extends XML<T>> {

        private final List<MarkupLanguage> nodeList = new ArrayList<>();

        private final Map<String, String> attributeList = new HashMap<>();

        public T attr(final String key, final String value) {
            if (value != null) {
                this.attributeList.put(key, value);
            } 
            return (T) this;
        }

        public T appendTo(final T node) {
            node.add(this);
            return (T) this;
        }
        ...
    } 

My problem is typing of these clauses - I am getting unchecked cast for "return (T) this;"

and also when I try to use the XML class by itself:

    final XML url = new XML("url");
    new XML("loc")
        .add("http://goout.cz")
        .appendTo(url);

I am getting:

    Unchecked cast to call appendTo(T) as a member of raw type XML.

How can I improve my code to get better typing?

Just type it:

final XML<T> url = new XML<T>("url");
new XML<T>("loc")
    .add("http://goout.cz")
    .appendTo(url);

It also looks like you really want to use XML<T> for your methods, not T , so you can avoid the casts (but I could be wrong):

public XML<T> attr(String key, String value) {
    if (value != null) {
        this.attributeList.put(key, value);
    } 
    return this;
}

public XML<T> appendTo(XML<T> node) {
    node.add(this);
    return this;
}

What about the approach below (simple inheritance plus generic methods):

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

public class XmlTest {

    @Test
    public void test() {
        XMLFirstSubclass url = new XMLFirstSubclass("url");
        XMLSecondSubclass xmlSecondSubclassInstance = new XMLSecondSubclass(
                "loc").add("http://goout.cz").appendTo(url);
    }
}

abstract class XML {

    private final List<String> texts = new ArrayList<String>();

    private final List<XML> nodes = new ArrayList<XML>();

    private final String nodeName;

    protected XML(String nodeName) {
        this.nodeName = nodeName;
    }

    @SuppressWarnings("unchecked")
    public <R extends XML> R add(String text) {
        texts.add(text);
        return (R) this;
    }

    @SuppressWarnings("unchecked")
    public <R extends XML, T extends XML> R add(T node) {
        nodes.add(node);
        return (R) this;
    }

    @SuppressWarnings("unchecked")
    public <R extends XML, T extends XML> R appendTo(T node) {
        node.add(this);
        return (R) this;
    }
}

class XMLFirstSubclass extends XML {
    public XMLFirstSubclass(String nodeName) {
        super(nodeName);
    }
}

class XMLSecondSubclass extends XML {
    public XMLSecondSubclass(String nodeName) {
        super(nodeName);
    }
}

Note that the generic methods allow to get a node from one type T and return the instance's type R, which can be different than the argument's type. T can be different than R, but both inherit XML.

Comments about the approach presented in the question

The approach that you're using until now can lead to strange situtations. Let's illustrate this with an example.

Below, we try to write the first class that specializes XML:

public class XMLFirstSubclass extends XML<OtherXMLSubclass> { ... }

If we're writing the first XML subclass, the only possible values to OtherXMLSubclass is XMLFirstSubclass or not declaring the generic type at all.

First option:

public class XMLFirstSubclass extends XML<XMLFirstSubclass> { ... }

Second:

public class XMLFirstSubclass extends XML { ... }

If you chose to use generics in your class design, the second option seems bad.
Taking a closer look into the first option, it opens the possibility of getting subclasses like:

class XMLSecondSubclass extends XML<XMLFirstSubclass> {
    ... 
}

Note that this compiles perfectly, but will cause class cast exceptions in XMLSecondSubclass method calls at runtime.

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