Matthias
Matthias

Reputation: 387

a more extensive recursion/generics question

Based on the question asked and answered here, I have a second, more complex one (at least in my opinion). To make reading (and answering) easier, I will, however, restate the complete code example here:

Let us begin by defining a set of classes/interfaces:

public interface Node<NT extends Node<NT, ET>, ET extends Edge<NT, ET>> {
  /* internal datastructures here */
}

public interface Edge<NT extends Node<NT, ET>, ET extends Edge<NT, ET>> {
  /* internal datastructures here */
}

public interface Graph<NT extends Node<NT, ET>, ET extends Edge<NT, ET>> {
  /* internal datastructures here */
}

public class JunctionNode implements Node<JunctionNode, RoadEdge> {
}

public class RoadEdge implements Edge<JunctionNode, RoadEdge> {
}

public class StreetGraph implements Graph<JunctionNode, RoadEdge> {
}

public class PTNode implements Node<PTNode, PTEdge> {
}

public class PTEdge implements Edge<PTNode, PTEdge> {
}

public class PTGraph implements Graph<PTNode, PTEdge> {
}

I now need to define an intermodal graph, i.e. a graph containing PTEdges as well as RoadEdges. In my opinion, I would do this by stating

public class IntermodalGraph implements Graph<Node, Edge> {
}

Here the compiler complaints on Node and Edge, since Node is defined to have two type parameters whereupon one is derived from the second type (here Edge). This means, I would have to state

public class IntermodalGraph implements Graph<Node<Node, Edge>, Edge> {
}

So far, the first type parameter (Node) is ok, but the second one (Edge) fails since edges takes two type parameters again, the first one derived from Node. So, I would write

public class IntermodalGraph implements Graph<Node<Node, Edge>, Edge<Node<Node, Edge>>, Edge> {
}

Now, the second type parameter is ok, but the first one is (obviously) "bad" again.

In the end, I would like to achieve some code like

IntermodalGraph ig = new IntermodalGraph();
ig.add(new PTEdge());
ig.add(new RoadEdge());

Set<Edge> edges = ig.getEdges();

So does anybody have an idea how to achieve this while keeping type safe?

lg, Matthias

Upvotes: 3

Views: 185

Answers (2)

Ben Schulz
Ben Schulz

Reputation: 6181

As already pointed out by toto, you define a type checkable contract and then proceed to break it -- and the compiler intervenes. A type safe solution is to simply split the contract in two, a weaker and a stricter one: A Graph<N extends Node<?, ?>, E extends Edge<?, ?>> interface as well as a StrictGraph<N extends Node<N, E>, E extends Edge<N, E>> interface which extends Graph<N, E>.

Upvotes: 0

Yanick Rochon
Yanick Rochon

Reputation: 53531

Since your classes already specify the generics, you could declare your IntermodalGraph class as

public class IntermodalGraph<NT extends Node<NT, ET>, ET extends Edge<NT, ET>> implements Graph<NT, ET> {
}

And this should not throw any warnings.

** EDIT **

You can't accomplish what you want, that is having

IntermodalGraph ig = new IntermodalGraph();
ig.add(new PTEdge());
ig.add(new RoadEdge());

Set<Edge> edges = ig.getEdges();

not throw any unchecked warnings, unless you add @SuppressWarnings("unchecked") annotations. But, it is still valid Java syntax and will work; the generic NT will default to Node and ET to Edge

Upvotes: 2

Related Questions