Reputation: 65
So I have this linked list class:
public class LinkedList {
private LLNode rootNode;
public Node FindItemByData(String data) {
if(rootNode == null)
return null;
else
return rootNode.findItemByData(data);
}
And this node class:
public class LLNode {
LLNode tail; //tail node
Node data; //some data
public LLNode(LLNode tail, Node data)
{
this.tail = tail;
this.data = data;
}
public Node findItemByData(String data) {
if(this.data.name.equals(data))
return this.data;
else
return this.tail.findItemByData(data);
}
I want to re-use the linked list for storing edges in a graph within each Node data
of the LLNode. I had a go at replacing the type using Generic Types but this breaks the functionality of the findItemByData
function as it relies on data being explicitly declared as a Node.
Is there any way I can reuse this class for multiple types? Or should I not be referring to data.name
in a Generic Class?
Implementation context:
public class Graph {
//USE LINKED LIST
LinkedList Nodes;
//Node[] Nodes;
int noOfNodes;
public Graph() {
noOfNodes = 0;
//Nodes = new Node[25];
Nodes = new LinkedList();
}
public void AddNode(String name, int x, int y) {
//Nodes[noOfNodes++] = new Node(name,x,y);
Nodes.AddItem(new Node(name,x,y));
}
..
public class Node {
String name; //Node's name
int x,y; //Node's coords
LinkedList Adjacencies;
int noOfAdj = 0;
int size = 0;
public Node(String name, int x, int y) { //Constructor
this.name = name;
this.x = x;
this.y = y;
Adjacencies = new LinkedList();
}
public void addAdjacency(String dest, double distance) {
Adjacencies.AddItem(new Edge(this.name, dest, distance)); //I want to do this
}
}
Edit: attempt at using generics:
public class LinkedList<T> {
private LLNode rootNode;
public T FindItemByData(String data) {
if(rootNode == null)
return null;
else
return rootNode.findItemByData(data);
}
}
public class LLNode<T> {
LLNode tail; //tail node
T data; //some data
public LLNode(LLNode tail, T data)
{
this.tail = tail;
this.data = data;
}
public T findItemByData(String data) {
if(this.data.name.equals(data))
return (T) this.data;
else
return (T) this.tail.findItemByData(data);
}
}
public class Graph {
LinkedList<Node> Nodes;
int noOfNodes;
public Graph() {
noOfNodes = 0;
Nodes = new LinkedList();
}
public void AddNode(String name, int x, int y) {
Nodes.AddItem(new Node(name,x,y));
}
}
public class Node {
String name; //Node's name
int x,y; //Node's coords
LinkedList<Edge> Adjacencies;
int noOfAdj = 0;
int size = 0;
public Node(String name, int x, int y) { //Constructor
this.name = name;
this.x = x;
this.y = y;
Adjacencies = new LinkedList();
}
public void addAdjacency(String dest, double distance) {
Adjacencies.AddItem(new Edge(this.name, dest, distance)); //I want to do this
}
}
Upvotes: 2
Views: 114
Reputation: 4945
I think your attempt at generics was actually pretty close. It has the advantage of enabling the compiler to tell whether a particular LinkedList
holds Nodes
or Edges
. So I'd suggest that you keep the generic parameterization of LinkedList
, but make it mean something a little different. The T
should be the kind of data that is held in the LLNode
. The trick is that your data has a String name
, so you need something from which to extend in order to get that name
.
interface Named {
String getName();
}
public class LinkedList<T extends Named> {
private LLNode<T> rootNode;
// etc.
}
public class LLNode<T extends Named> {
LLNode<T> tail; //tail node
T data; //some data
public LLNode(LLNode<T> tail, T data) {
this.tail = tail;
this.data = data;
}
public T findItemByData(String data) {
if(this.data.getName().equals(data))
return this.data;
else
return this.tail.findItemByData(data);
}
// etc.
}
Now you can instantiate a LinkedList
that holds just Nodes
, and another that holds just Edges
, assuming that both Node
and Edge
implement Named
.
I've mentioned it but want to reemphasize: this is superior to an approach that just uses a supertype of Node
and Edge
within LinkedList
. In that case, you could put both Node
and Edge
instances in the same LinkedList
and the compiler wouldn't be able to warn you. The generic approach gives you the ability to create LinkedList
instances that are restricted to Node
, or Edge
, or unrestricted (new LinkedList<Named>()
). In all cases the compiler will give you the support you need to keep the lists straight.
Upvotes: 1
Reputation: 16641
Basically you are saying that Node and Edge should abide to the same contract. To do this you should both let them implement an interface that contains all the methods that are part of that contract.
In this case this will probably only be getData()
. Then use this interface for your methods which can take any Edge or Node.
Another way to do this would be to make Edge an extension of Node. public class Edge extends Node
. Then you can use it whereever a Node is needed.
Upvotes: 1