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.