Повторно использовать связанный список для другого типа (универсальные типы)


Итак, у меня есть этот класс связанного списка:

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 2

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>()). Во всех случаях компилятор предоставит вам поддержку, необходимую для сохранения правильности списков.