Generic wildcard in Java - java

I'm writing a generic class:
public class Node<T> {
private Node<T> parent = null;
private List<? extends Node<T>> children = null;
public Node<T> getParent() {
return parent;
}
public void setParent(Node<T> parent) {
if(this.parent != null){
// Remove current parent's children references
this.parent.getChildren().remove(this);
}
// Add references
this.parent = parent;
parent.getChildren().add(this);
}
public List<? extends Node<T>> getChildren() {
return children;
}
}
I want some other class which subclass this Node. This code cannot be compiled with the error on line parent.getChildren().add(this);. Because I declared getChildren() with List<? extends Node<T>> as return type, and 'this' is type Node<T>.
Is there anyway to solve this?

Declare the list as:
List<Node<T>> children
You may still put instances of subclasses in the list.
If you leave it as an unknown type, the compiler can't ensure which class it is typed as. Eg it might be typed as SubClassA, but you're adding SubClassB and it has no way to know based on the declared type, which is all the compiler has to go on. At runtime, while the type of list and child might match, the compiler can't assert.

Related

Implementing parent-child relationship with generics

I am trying to make a container(parent) object where you can specify type of objects(children) it can contain.
Similarly, you can specify type of parent of the child.
Parent and child needs to communicate in the future, so I need them both to have object references to each other.
This code is a simpler representation of the actual code in my app.
class Parent<T extends Child> {
ArrayList<T> childObjects;
public void addChildChild(T newChild) {
childObjects.add(newChild);
newChild.setParent(this);
}
public void newChildConnected(T connectedChild) {
System.out.println("Child connected");
}
}
class Child <T extends Parent> {
T parentObject;
public void setParent(T newParent) {
parentObject = newParent;
parentObject.newChildConnected(this);
}
}
My IDE says:
Unchecked call to 'newChildConnected(T)' as a member of raw type 'test.Parent'
I have been trying different ways to make it better with wildcards and stuff, but I this is best I can do.
So what is the correct way of implementing such a behavior?
My goal is to be able to specify the child type for parent and parent type for child and do it in the way that both children and parent are able to use functionality of each other without using the intanceof() operator and casting. (that's why I use generics after all)
Is is even possible in Java?
Your usage of generic types creates a circular type reference. If the type(or interface/base class) of the parent and child are the same, use a tree structure within a single class:
class MyObject<T> {
T parentObject;
ArrayList<T> childObjects = new ArrayList();
public void addChildChild(T newChild) {
childObjects.add(newChild);
newChild.setParent(this);
}
public void newChildConnected(T connectedChild) {
System.out.println("Child connected");
}
public void setParent(T newParent) {
parentObject = newParent;
parentObject.newChildConnected(this);
}
When using this class you must check for top level objects where parentObject == null and leaf objects where childObjects.size()==0.
If there is no common interface or base class, this is not possible to do safely.
As #Strom pointed out correctly, this cannot be done in a typesafe way without base classes or interfaces.
If you can extend a class/interface, a typesafe solution without any casts would look like this:
interface BaseParent<P extends BaseParent<P, C>, C extends BaseChild<P, C>> {
List<C> getChildren();
void setChildren(List<C> children);
P self();
default void addChild(C child) {
if (child.getParent() == null) {
child.setParent(self());
}
final ArrayList<C> newChildren = new ArrayList<>(getChildren());
newChildren.add(child);
setChildren(newChildren);
}
}
interface BaseChild<P extends BaseParent<P, C>, C extends BaseChild<P, C>> {
void setParent(P parent);
P getParent();
}
final class Parent implements BaseParent<Parent, Child> {
private List<Child> children = new ArrayList<>();
#Override
public List<Child> getChildren() {
return children;
}
#Override
public void setChildren(List<Child> children) {
this.children = children;
}
#Override
public Parent self() {
return this;
}
}
final class Child implements BaseChild<Parent, Child> {
private Parent parent;
public Child(Parent parent) {
this.parent = parent;
this.parent.addChild(this);
}
#Override
public void setParent(Parent parent) {
this.parent = parent;
}
#Override
public Parent getParent() {
return parent;
}
}
The solution uses "recursive" generics for type safety and a self-type reference to avoid casts. Both of these approaches have caveats and are not entirely safe because you have to rely on the implementor of the base/class interface to return the correct self type and define correct type parameters, but should be good enough for internal APIs.

Why can I not assign to this generic variable?

I'm trying to implement a tree such that the nodes are generic:
public class BinarySearchTree<U extends BinarySearchTree.Node, T extends Comparable<? super T>>
{
public U root;
...
public class Node {
T data;
U left;
U right;
public Node(T data) {
this.data = data;
left = null;
right = null;
}
}
}
But when I try to do an assignment in one of my methodsI get a "Expected U but found BinarySearchTree.Node:
public void display(U root) {
if (root != null) {
display(root.left); // gives compile time error
System.out.print(" " + root.data);
display(root.right); // gives compile time error
}
}
Since Node is not static, every Node instance belongs to a containing BinarySearchTree instance. But it is not possible with generics to specify to which one it belongs. So if you specify BinarySearchTree.Node it can belong to any BinarySearchTree. Since you also used a raw type (don't ignore the warnings!) it is not known what U is. Therefore, Java cannot check that root.left and root.right are of the same type, which is why you get the error.
You can fix the compile error by specifying the class as follows:
class BinarySearchTree<U extends BinarySearchTree<U,T>.Node, T extends Comparable<? super T>>
{ ... }
but this does not fix the underlying problem that the nodes can belong to any containing BinarySearchTree, which probably is not what you want.
If you really want use a generic node type, you could make it static you, so that it doesn't belong to containing instances anymore:
public static class Node<U,T>
{
...
}
But I question the need to have a generic node class in the first place. Usually, such nodes are considered an implementation detail and you want to have full control over creating them from within your class. That cannot work with a generic node because your class doesn't know what kind of nodes to instantiate.
So I would define your BinarySearchTree class as follows:
public class BinarySearchTree<T extends Comparable<? super T>>
{
private Node<T> root;
private static class Node<T>
{
T data;
Node<T> left;
Node<T> right;
}
...
}
You should rewrite the class definition to
public class BinarySearchTree<U extends BinarySearchTree<U, T>.Node, T extends Comparable<? super T>>

From a Generic Tree To a Domain Specific Tree

I want to use a domain specific tree DomainTree consisting of Domain specific Nodes DomainNode, but keep all generic functions in template classes Tree and Node. First I started with the templates Tree<T> and Node<T> (where T is the type of a nodes data). The DomainTree was then working with the Node<T> interface, which was not what I wanted. It should work on DomainNode objects instead.
To cope with that, I changed the generic tree's template parameter to Tree<N extends Node<?>> (the implementation below). Now I can work with the DomainNode by instantiating the tree as DomainTree<DomainNode>.
Still, I get a compilation error at (1) because getChildren() returns a list of Node<T>, which doesn't seem to be convertible to a list of N, though I made sure that N extends Node<?>.
Why is this not working and how can I design it, so that the DomainTree can work with DomainNodes?
Generic Tree
import java.util.ArrayList;
import java.util.List;
class Tree<N extends Node<?>> {
public N rootElement;
public List<N> toList() {
List<N> list = new ArrayList<N>();
walk(rootElement, list);
return list;
}
private void walk(N element, List<N> list) {
list.add(element);
List<N> children = element.getChildren(); // (1) Cannot convert from List<Node<T>> to List<T>
for (N data : children) {
walk(data, list);
}
}
}
class Node<T> {
public T data;
public List<Node<T>> children;
public List<Node<T>> getChildren() {
if (this.children == null) {
return new ArrayList<Node<T>>();
}
return this.children;
}
public void addChild(Node<T> child) {
if (children == null) {
children = new ArrayList<Node<T>>();
}
children.add(child);
}
}
Problemspecific Tree
class DomainTree extends Tree<DomainNode> {
public void build() {
for (DomainNode node : toList()) {
// process...
}
}
}
class DomainNode extends Node<String> {
}
The problem with the code as it stands is that for a given Node<T>, the compiler has no way of knowing that the type of the List returned from toList() is the same Node<T> as the class itself.
What you need is a self-referencing generic type:
class Node<T, N extends Node<T, N>> {
public T data;
public List<N> children;
public List<N> getChildren() {
return children == null ? Collections.<N>emptyList() : children;
}
public void addChild(N child) {
if (children == null) {
children = new ArrayList<N>();
}
children.add(child);
}
}
Now the type returned from toList() is the same type as the type itself.
Then DomainNode becomes:
class DomainNode extends Node<String, DomainNode> {
//
}
And the signature of of Tree changes slightly to become:
class Tree<N extends Node<?, N>> {
And your usage example now compiles:
class DomainTree extends Tree<DomainNode> {
public void build() {
for (DomainNode node : toList()) {
// process...
}
}
}
I added in a couple of other efficiencies too.
generics hold some surprises if you are not really into it. At first keep in mind that there is a type erasure and the compiler and the runtime thus see different things. Roughly spoken this also limits the compiler abilities in analyzing source code.
Note that there is really a difference between a List<Node<N>> and a List<N>. Hence even with N extends Node<?> the assignment 'List children = element.getChildren();' is inherently broken.
Furthermore with your declaration of Tree<N extends Node<?>>, you would expect that you can write something like List<Node<?>> l2 = element.getChildren();. Unfortunately this does not work due to some subleteties of generics. For example if you change your code to class Tree<N extends Node<N>> (this is probably not what you intended) you can write List<Node<N>> l2 = element.getChildren();.
I recommend to study the Sun Certified Java Programmer Study Guide for Java 6 (or a newer version) or something similar which is really helpful about generics.
From your code I got the impression that you mix different abstraction layers as there is the T data in the Node<T> class and in the for each loop the element is called N data. However in the loop you have a node N extends Node<?> which would be completely different from the T data. Hence the intention for your code remains a bit unclear for me. A working draft if your code as fixed version is here (Eclipse Luna, JDK 6)
package generics.tree;
import java.util.ArrayList;
import java.util.List;
class Tree<T> {
public Node<T> rootElement;
public List<Node<T>> toList() {
List<Node<T>> list = new ArrayList<Node<T>>();
walk(rootElement, list);
return list;
}
private void walk(Node<T> element, List<Node<T>> list) {
list.add(element);
List<Node<T>> children = element.getChildren(); // (1) Cannot convert from List<Node<T>> to List<T>
for (Node<T> data : children) {
walk(data, list);
}
}
}
class Node<T> {
public T data;
public List<Node<T>> children;
public List<Node<T>> getChildren() {
if (this.children == null) {
return new ArrayList<Node<T>>();
}
return this.children;
}
public void addChild(Node<T> child) {
if (children == null) {
children = new ArrayList<Node<T>>();
}
children.add(child);
}
}
class DomainTree extends Tree<String> {
public void build() {
for (Node<String> node : toList()) { // changed!
// process...
}
}
}
class DomainNode extends Node<String> {
}

Enforce class declarations with Generics

I want to implement an abstract Hierarchy class. Any Hierarchy object should have a parent (could be null) and a set of children, both of the exact same type as the concrete implementation of Hierarchy. Can this be done with generics, and if not is there any mechanism to enforce this relationship?
Here's what I was thinking so far, but there are issues:
public abstract class Hierarchy<T extends Hierarchy<T>> {
private T parent;
private Set<T> children;
public T getRoot() {
if( parent == null ) {
return this;
} else {
return parent.getRoot();
}
}
}
The problem is with return this. It gives a compile error because this is of type Hierarchy, not T. I can't cast it, because I don't actually guarantee that this is of type T. The following declaration would compile just fine:
public class B extends Hierarchy<B> {...}
public class A extends Hierarchy<B> {...}
So is there any way I can disallow A extends Hierarchy<B> declarations?
So is there any way I can disallow A extends Hierarchy
declarations?
No, it is not possible.
You can do what you want with something like this:
public abstract class Hierarchy<T> {
private T parent;
private Set<T> children;
public static <E extends Hierarchy<E>> E getRoot(E x) {
while (x.parent != null) {
x = x.parent;
}
return x;
}
}

Write a tree class in Java where each level has a unique object type

I need to write a tree class in Java where each level has a unique object type. The way it is written below does not take advantage of generics and causes alot of duplicate code. Is there a way to write this with Generics ?
public class NodeB {
private String nodeValue;
//private List<NodeB> childNodes;
// constructors
// getters/setters
}
public class NodeA {
private String value;
private List<NodeB> childNodes;
// constructors
// getters/setters
}
public class Tree {
private String value;
private List<NodeA> childNodes;
// constructors
// tree methods
}
This is simplistic implementation, but enough to give general idea:
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class GenericNode {
public static abstract class AbstractNode<V, N> {
private V value;
private List<N> children;
public AbstractNode(V value, N... children) {
this.value = value;
this.children = children != null ? Arrays.asList(children)
: Collections.<N> emptyList();
}
public V getValue() {
return value;
}
public List<N> getChildren() {
return children;
}
public int getNumberOfChildren() {
return children.size();
}
#Override
public String toString() {
return value.toString() + "->" + children.toString();
}
}
// leaf node type, ignore type of children
public static class NodeB extends AbstractNode<String, Object> {
public NodeB(String value, Object... nodes) {
super(value, nodes);
}
}
// example of typical node in the mid of tree
public static class NodeA extends AbstractNode<String, NodeB> {
public NodeA(String value, NodeB... nodes) {
super(value, nodes);
}
}
// top level node type
public static class Tree extends AbstractNode<String, NodeA> {
public Tree(String value, NodeA... nodes) {
super(value, nodes);
}
}
#SuppressWarnings({ "rawtypes", "unchecked" })
public static <V, N extends AbstractNode> int getNodeCount(
AbstractNode<V, N> node) {
int nodeCount = node.getChildren().size();
for (N child : node.getChildren()) {
nodeCount += getNodeCount(child);
}
return nodeCount;
}
public static void main(String[] args) {
NodeB nodeB1 = new NodeB("Leaf node 1");
NodeB nodeB2 = new NodeB("Leaf node 2");
NodeA nodeA = new NodeA("Node with children", nodeB1, nodeB2);
NodeA emptyNodeA = new NodeA("Empty node");
Tree tree = new Tree("Tree", nodeA, emptyNodeA);
System.out.println(tree);
System.out.println(1 + getNodeCount(tree));
}
}
You could make N and V types implement specific interfaces so it will be possible to call some common operations on values and/or children.
EDIT: updated implementation with recursive method for node count retrieval
All you need is a Pair<A, B>. Example of trees:
Pair<A, Pair<B, C>>
Pair<Pair<A, B>, Pair<C, D>>
Pair<Pair<Pair<A, B>, Pair<C, D>>, Pair<Pair<E, F>, Pair<G, H>>
ps: don't do this. :)
This is an ideal spot for everything inheriting from "Node", but even that is unnecessary.\
What you probably want is a single generic "Node" object that contains references to your different classes (use composition before inheritance).
At that point, each of your different classes probably has something that can be done to them (otherwise why are they all in the same data structure?) Have them implement a common interface with this common functionality. The node class can delegate to this interface, or some other class can extract the class by this interface and act on it.
This would be better than trying to force something to also BE a node--do one simple thing and do it well.
--edit--
I can't really add an example that is relevant to you because you didn't post anything about your scenario.
But let's say that you have these different classes A, B * C. First of all are they related AT ALL aside from all being children of Object? Let's say they all implement interface "Iface". (If not, you can just replace Iface with "Object", but this really implies a bad design.)
Anyway, your "Node" object is just one object--
public class Node {
private List<node> children;
private Iface myObject;
... setters, getters, tree implementation, tree navigation, related garbage...
}
Now this is enough to create your tree. One thing that you might be able to do to make things smoother, have "Node implements Iface" and delegate any calls to it's object. For instance, if Iface contains an eat(Food foodtype) method, your node could implement Iface and have a method:
public void eat(Food foodtype) {
myObject.eat(foodtype);
}
This would make the "Node" class act as though it was the class it contained.
By the way--another relatively good idea at this point would be to make myObject "private final" and ensure it is not null in the constructor. That way you would always know it was set and none of your delegated members would have to do null checks.
I don't think generics are going to help you much in this case. Instead of having a different class for each level in the tree. What about one node class that has children and store a different class on each level. That should help eliminate a lot of the duplication.
I'm fairly new to Java, so this might have issues I'm not aware of, but it seems to work on a simple level at least.
Define your main Node class - this one will be the root of the tree.
public class NodeA {
private String _value;
private ArrayList<NodeA> _children;
private int _depth;
public NodeA (String value, int depth) {
_value = value;
_children = new ArrayList<NodeA>();
_depth = depth;
}
//probably want getters for _children and _value here
//this makes a new child, with its type depending on the depth of the tree it will
//be placed at. NodeB and NodeC will both inherit from NodeA
public void add(String value) {
switch (_depth) {
case 0:
_children.add(new NodeB(value, _depth+1));
break;
case 1:
_children.add(new NodeC(value, _depth+1));
break;
}
}
The add() method is going to create a new child for the node using the specified value. If you initialize the root of the tree as a NodeA with depth 0, you can add children to nodes and the tree should end up populated so that the next level contains all NodeB's, and the next all NodeC's. The code for NodeB and NodeC is super simple and could be replicated to create an arbitrary amount of Node levels (here is that code).
public class NodeB extends NodeA {
public NodeB(String value, int depth) {
super(value, depth);
}
//nothing else needed!
The code for NodeC is identical, except for the obvious replacements of B's with C's.
Hope this helps / is the kind of answer you wanted!

Categories