2009-10-13 57 views
4

主編:」我收到了來自‘埃裏克森’一個非常中肯的答案,但是這是沒有明確涵蓋在我原來的例子,並且不與解決的側問題(上鑄造?)他我已經擴展了這個例子來覆蓋這個問題,並且我已經將它包含在這篇文章的最後。感謝您的幫助。遞歸通用用法

我目前正面臨與相關的Java泛型有關的問題的東西,被稱爲"Curiously Recurring Generic Pattern"。我想我已經找到了解決方案,從喬恩斯基特閱讀這個問題的答案後"java enum definition"。不過,我發現自己不同的問題時,我試圖將其應用於我的代碼中。

我拿出哪裏出了問題我現在面臨一個出現「小」的例子。我希望它能清楚地說明我的問題。

示例說明:我想構建一個圖表,其中節點類型可以變化。我定義了一個抽象類節點,其中定義了一些基本方法,以及實現這些方法,即ConcreteNode的具體類。我也創建了一個名爲City的ConcreteNode的專業化。

在給定的圖中,一個重要的要求是,所有的元件應被由相同類型或它的亞型,即ConcreteNode的曲線圖中只能有ConcreteNodes 城市。

這些都是我的類的定義:

abstract class Node<T extends Node<T>> 
class ConcreteNode<T extends ConcreteNode<T>> extends Node<T> 
class City extends ConcreteNode<City> 

這些定義利用「定期通用模式」在枚舉類的定義還發現:

Class Enum<E extends Enum<E>> 

問題:我遇到了使用這些類的問題。我沒有問題,如果我要留在層次結構中的城市級別,即市連接到市,但在嘗試訪問其他類當我在巨大問題。

在下面的代碼,我的問題,可以看出在GraphUtil的方法簽名:

  1. addNewNeighbors1a使用原始類型的節點,但至少它的工作原理。
  2. addNewNeighbors1b使用類型節點,但它並不在所有編譯(誤差被包括在代碼)。
  3. addNewNeighbors1c使用Node的更復雜的參數,我希望能夠工作,但它不能編譯(錯誤包含在代碼中)。
  4. addNewNeighbors3用來節點複雜的參數,但它並沒有重新編譯,即使參數是節點newNode相同。

在綜合中,我的問題是如何上傳這些參數化爲自己的泛型?

我很樂意爲GraphUtil的方法獲得最佳簽名方面的幫助,假設這些方法將位於一個對City或甚至ConcreteNode不瞭解的庫中。

謝謝大家。

這裏的例子

package test.city; 

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Collection; 

public class TestCity { 
    abstract class Node<T extends Node<T>> { 
    public abstract void addNeighbor(T n); 
    public abstract void addNeighbors(Collection<? extends T> nodes); 
    public abstract Collection<T> neighbors(); 
    } 

    class ConcreteNode<T extends ConcreteNode<T>> extends Node<T> { 
    protected Collection<T> _neighbors = new ArrayList<T>(); 

    @Override 
    public void addNeighbor(T n) { 
     _neighbors.add(n); 
    } 

    @Override 
    public void addNeighbors(Collection<? extends T> nodes) { 
     _neighbors.addAll(nodes); 
    } 

    @Override 
    public Collection<T> neighbors() { 
     return _neighbors; 
    } 
    } 

    class City extends ConcreteNode<City> { 
    protected String _name; 

    public City(String name) { 
     _name = name; 
    } 

    @Override 
    public String toString() { 
     return _name; 
    } 
    } 

    public TestCity() { 
    City nyc = new City("NYC"); 
    nyc.addNeighbor(new City("Boston")); 
    nyc.addNeighbor(new City("Wash")); 

    GraphUtil.print("Printing cities", nyc.neighbors()); 

    GraphUtil.printNeighbors1(nyc); 
    GraphUtil.printNeighbors2(nyc); 
    GraphUtil.printNeighbors3(nyc); 
    GraphUtil.printNeighbors4(nyc); 
    GraphUtil.addNewNeighbors1a(nyc, new City("Miami")); 
    GraphUtil.addNewNeighbors2(nyc, new City("NewOr")); 
    GraphUtil.addNewNeighbors3(nyc, new City("Dallas")); 
    } 

    static class GraphUtil { 
    static void printNeighbors1(Node<?> node) { 
     print("Nodes", node.neighbors()); 
    } 

    static void printNeighbors2(ConcreteNode<?> node) { 
     print("Concrete nodes", node.neighbors()); 
    } 

    static void printNeighbors3(Node<? extends Node<?>> node) { 
     print("Nodes2", node.neighbors()); 
    } 

    static void printNeighbors4(ConcreteNode<? extends ConcreteNode<?>> node) { 
     print("Concrete nodes2", node.neighbors()); 
    } 

    static void addNewNeighbors1a(Node node, City newNode) { 
     node.addNeighbor(newNode); 
     print("Add city to node", node.neighbors()); 
    } 

    static void addNewNeighbors1b(Node<?> node, City newNode) { 
     // node.addNeighbor(newNode); <---- DOES NOT COMPILE!!! 
     // The method addNeighbor(capture#8-of ?) in the type 
     // TestCity.Node<capture#8-of ?> 
     // is not applicable for the arguments (TestCity.City) 
    } 

    static void addNewNeighbors1c(Node<? extends Node<?>> node, City newNode) { 
     // node.addNeighbor(newNode); <---- DOES NOT COMPILE!!! 
     // The method addNeighbor(capture#9-of ? extends TestCity.Node<?>) 
     // in the type 
     // TestCity.Node<capture#9-of ? extends TestCity.Node<?>> is not 
     // applicable for the arguments (TestCity.City) 

    } 

    static void addNewNeighbors2(Node node, ConcreteNode newNode) { 
     node.addNeighbor(newNode); 
     print("Add concrete node to node", node.neighbors()); 
    } 

    static void addNewNeighbors3(Node<? extends Node<?>> node, 
     Node<? extends Node<?>> newNode) { 
     // node.addNeighbor(newNode); <---- DOES NOT COMPILE!!! 
     // The method addNeighbor(capture#8-of ? extends TestCity.Node<?>) 
     // in the type 
     // TestCity.Node<capture#8-of ? extends TestCity.Node<?>> is not 
     // applicable for the arguments 
     // (TestCity.Node<capture#10-of ? extends TestCity.Node<?>>) 
    } 

    static void print(String msg, Collection<?> col) { 
     System.out.println(msg + ": " + Arrays.toString(col.toArray())); 
    } 
    } 

    public static void main(String[] args) { 
    new TestCity(); 
    } 

} 

的完整的代碼運行這段代碼的輸出如下(沒有驚喜可言):

Printing cities: [Boston, Wash] 
Nodes: [Boston, Wash] 
Concrete nodes: [Boston, Wash] 
Nodes2: [Boston, Wash] 
Concrete nodes2: [Boston, Wash] 
Add city to node: [Boston, Wash, Miami] 
Add concrete node to node: [Boston, Wash, Miami, NewOr] 

問題的第二部分

有一個相關的問題,我沒有包括在原來的實例因爲我認爲解決方案也適用。

我現在已經添加了以下方法GraphUtil:

static <T extends Node<T>> T getSomeNeighbor(T node) { 
    return node.neighbors().iterator().next(); 
} 

而且從我的主類我想以下幾點:

City someCity = GraphUtil.getSomeNeighbor(nyc); 
someCity.addNeighbor(new City("London")); // OK 

ConcreteNode someCN1 = GraphUtil.getSomeNeighbor(nyc); 
someCN1.addNeighbor(new City("Paris")); // OK, but raw 

ConcreteNode<?> someCN2 = GraphUtil.getSomeNeighbor(nyc); 
someCN2.addNeighbor(new City("Berlin")); // Does not compile 

ConcreteNode<?> nc = new City(""); 
nc.addNeighbor(new City("Bern")); // Does not compile 

第一種情況的工作,因爲我知道返回的具體類型,並且與參數中提供的類型一致。

在第二和第三種情況下,我假設我不知道城市類型。第二種情況有效,但我使用的是原始類型ConcreteNode。

在第三種情況下,第二行中出現編譯錯誤:「TestCity.ConcreteNode類型中的方法addNeighbor(capture#3 of?)不適用於參數(TestCity.City)。 「

在這個例子中,我使用'new City(「 - 」)'作爲參數,因爲我不知道如何上傳它們。在第四種情況下,我試圖將City上傳到ConcreteNode,但失敗了。目前的編譯器錯誤如下:「的方法addNeighbor(捕獲#4的?)在類型TestCity.ConcreteNode不適用於參數(TestCity.City)」

問題:

  1. 如何在不知道城市類型的情況下修復情況2和3?
  2. 如何將City上傳到ConcreteNode(或Node)?

感謝您的幫助。

+0

對不起,您更新後的問題中的示例過於設計,無法幫助我設想出現問題的地方。在你所有的例子中,你都知道'addNeighbor'的參數類型(它總是'City'')。你能不能展示你真的想寫的方法 - 也就是說,參數的類型沒有被靜態聲明?您可能需要展示如何調用此方法。 – erickson 2009-10-14 15:47:03

回答

2

您可以製作通用方法以及泛型類型。使用這些,問題方法GraphUtils可以固定這樣的:

static <T extends Node<T>> void addNewNeighbors1a(T node, T newNode) 
{ 
    node.addNeighbor(newNode); 
    print("Add city to node", node.neighbors()); 
} 

static <T extends Node<T>> void addNewNeighbors2(T node, T newNode) 
{ 
    node.addNeighbor(newNode); 
    print("Add concrete node to node", node.neighbors()); 
} 

嘿,等待一秒鐘&hellip;那些是相同的方法!

事實證明,由於它們只依賴於Node的接口,所以您只需要其中的一個來處理任何Node實現。

在路上,你可能會覺得有必要改變Node界面是這樣的:

public abstract <S extends T> void addNeighbor(S n); 
+0

嗨,謝謝你的回答:它非常清楚,它解決了我的部分問題。不過,還有一個方面沒有被你的答案所覆蓋。我已經更新了原文,以包含這一方面。如果你能幫助我,這將是一件好事。 非常感謝。 – nozebacle 2009-10-14 07:51:32

0

一,你在靜態方法通配符允許任何類型的節點加入,你的編譯器知道ISN」 t允許。假設你正在做蛋白質圖表(例如,我是這樣做的),並且你有一個帶有實例的Protein extends Node<Protein>類。你的通配符方法,他們工作,將允許我添加所述蛋白質。

作爲固定問題,嘗試限定沿這些線的添加方法:

public <U extends T> void add(U other) 

T由類參數定義,並且該方法允許添加的T的任何亞類中,由​​本地參數所限定U.這應該在一個方向上照顧你的問題,儘管你僅限於向超類添加子類。

+0

感謝您的回答。蛋白質圖譜確實是另一種應用。 正如erickson提到的那樣,添加U型添加對於支持未來類型非常有用。但是,我最初的問題是使用GraphUtil的方法,並且這些問題是按照他的建議解決的。 現在我已將問題的另一部分添加到原始帖子中。 – nozebacle 2009-10-14 07:58:56

1

對於你的第二部分,通配符又是一個問題。 ConcreteNode大概可能屬於City所不具備的某種類型 - 例如,ConcreteNode<Suburb>不會被City擴展 - 儘管您知道它是什麼,編譯器不會。

+0

你是對的....這意味着我的圖可以由不同種類的ConcreteNodes組成,不是嗎? 如果我知道情況並非如此,那麼我唯一的選擇是使用原始類型,還是使用鑄造? – nozebacle 2009-10-14 08:33:42

+0

我不會說這是唯一的選擇,但是你現在的設計已經把你裝箱了一點,是的。 – Carl 2009-10-14 09:48:21

2

對於部分1,I將使方法signiture

static <T extends Node<T>> void addNewNeighbors(Node<? super T> node, T newNode) 
{ 
    node.addNeighbor(newNode); 
} 

這樣一來,節點不必特別是相同的類型newNode。

對於第2部分,我會做類似的事情。

ConcreteNode<? super City> nc = new City(""); 
nc.addNeighbor(new City("Bern")); 
+0

第二部分沒有用,因爲我需要知道城市寫ConcreteNode <?超級城市「,我假設我不是。 – nozebacle 2009-10-17 11:32:54