Повторно использовать связанный список для другого типа (универсальные типы)
Итак, у меня есть этот класс связанного списка:
public class LinkedList {
private LLNode rootNode;
public Node FindItemByData(String data) {
if(rootNode == null)
return null;
else
return rootNode.findItemByData(data);
}
И этот класс узлов:
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);
}
Я хочу повторно использовать связанный список для хранения ребер в графе внутри каждого Node data
LLNode. Я попытался заменить тип с помощью универсальных типов, но это нарушает функциональность функции findItemByData
, поскольку она полагается на данные, явно объявленные как узел.
Есть ли способ повторно использовать этот класс для нескольких типов? Или я не должен ссылаться на data.name
В общем виде Класс?
Контекст реализации:
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: попытка использования дженериков:
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
}
}
2 ответа:
В основном вы говорите, что узел и ребро должны соблюдать один и тот же контракт. Для этого вы оба должны позволить им реализовать интерфейс, содержащий все методы, которые являются частью этого контракта.
В этом случае это, вероятно, будет только
getData()
. Затем используйте этот интерфейс для ваших методов, которые могут занять любое ребро или узел.Другой способ сделать это-сделать ребро расширением узла.
public class Edge extends Node
. Тогда вы можете использовать его везде, где требуется узел.
Я думаю, что ваша попытка создать дженерики была на самом деле довольно близка. Это имеет то преимущество, что компилятор может определить, содержит ли конкретный
LinkedList
Nodes
илиEdges
. Поэтому я бы предложил вам сохранить общую параметризациюLinkedList
, но сделать ее немного другой.T
должны быть такого рода данные, которые хранятся вLLNode
. Фокус в том, что ваши данные имеютString name
, поэтому вам нужно что-то, из чего можно расширить, чтобы получить это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. }
Теперь вы можете создайте экземпляр
Я уже упоминал об этом, но хочу еще раз подчеркнуть: это превосходит подход, который просто использует супертипLinkedList
, который содержит толькоNodes
, и другой, который содержит толькоEdges
, предполагая, что обаNode
иEdge
реализуютNamed
.Node
иEdge
внутриLinkedList
. В этом случае вы можете поместить оба экземпляраNode
иEdge
в один и тот жеLinkedList
, и компилятор не сможет предупредить вас. Универсальный подход дает вам возможность создаватьLinkedList
экземпляры, которые ограничены кNode
, илиEdge
, или неограниченно (new LinkedList<Named>()
). Во всех случаях компилятор предоставит вам поддержку, необходимую для сохранения правильности списков.