2015-08-28 64 views
-2
public class Foo<T extends Bar>{ 
    private Class<T> _type; 
    public Foo(Class<T> _type){ 
     this._type = _type; 
    } 
    public Collection<T> hypothetical(List<T> items){ //PROBLEMATIC 
     return dostuffWithItems(items); 
    } 
} 

用法:差異在通用參數多態性

Foo<? extends ChildBar> foo = new Foo<ChildBar>(ChildBar.class); 
List<ChildBar> items = (List<ChildBar>) foo.hypothetical(new ArrayList<ChildBar>()); //COMPILER ERROR: The method hypothetical(List<capture#2-of ?>) in the type Foo<capture#2-of ?> is not applicable for the arguments (List<ChildBar>) 

,編譯器將要麼接受
鑄造List<ChildBar> items參數List<?>
或改變hypothetical(List<T> items)簽名要麼
一)hypothetical(List<ChildBar> items)
b)hypothetical(List<? extends Bar> items)

但是,沒有任何替代方法確保假設方法的List items參數T type是Foo class T參數類型的等效運行時類型。我目前正在使用一種額外的方法來驗證參數類型。 在沒有額外邏輯的情況下,Java泛型構造中是否有更好的方法自動實現這一點?或者更好的是,爲什麼我不能將foo聲明爲Foo<? extends Bar>,然後在運行時填寫實際的類型參數?

+2

'foo.hypotethical(new ArrayList ())'有什麼問題? –

+1

你應該通過'類 clazz'作爲參數:'收集假設(列表項目,類 clazz)' –

+0

我發現3錯誤:1)'dostuffWithItems'是未定義的。 2)「假設」,「低估」和「假設」拼寫不一樣。 3)返回類型'Collection'不能被分配給'List'。 ---你也有這些_PROBLEMATIC_嗎?因爲一旦我修復這些3,我沒有編譯錯誤。 – Andreas

回答

-2

這似乎在Java中是不可能的。
Foo<? extends ChildBar> foo = new Foo<ChildBar>(ChildBar.class);
這使foo具有模糊的參數類型。 ChildBar顯然會成爲真正的事實上的參數類型。對foo.hypothetical()方法與List<ChildBar>的調用暴露了這種假設是不真實的。儘管foo.hypothetical僅接受包含foo參數類型元素的List<>參數,但它仍未能識別該參數是ChildBar對象的列表。

對於此用例,必須在foo聲明期間指定對象參數類型,以使其成爲foo運行時引用的組成部分。
Foo<ChildBar> foo = new Foo<ChildBar>(ChildBar.class);
所有符合條件的List<ChildBar> foo.hypothetical方法的參數將被正確接受爲攜帶foo的聲明參數類型的元素。

+0

*「這似乎在Java中是不可能的。」* - 並且它應該是「be」。如果這是合法的,您可以根據編譯時泛型類型模型構造代碼不安全的示例。 –

+0

您是否認爲Java作者故意讓它變得不可能,因爲用戶可以根據編譯時泛型類型模型構造不安全的代碼?您是否願意提供可能的代碼示例,這些代碼根據編譯時泛型類型模型而不安全,因爲可以使此功能成爲可能? – 000

+1

@StephenIsienyi是的,錯誤消息被提出是因爲否則用戶可以編寫,編譯和運行代碼,在運行時會出現類型錯誤。在你告訴我術語「通配符」和「捕獲」不是必需的之後,我不知道我應該如何向你解釋這一點。 –

5

我編輯你的代碼,並添加缺少的東西,使其編譯,我可以證實,只有有問題的部分是:

  • 缺少dostuffWithItems方法。
  • hypothetical方法名稱的拼寫錯誤。
  • 指定Collection<ChildBar>List<ChildBar>

前兩個很容易修復。

最後一個要求您更改API方法的更改或更改您調用它的代碼。這些都不是(IMO)問題。此外,如果這些類型是非泛型的,那麼您將得到所有這些錯誤是值得注意的。如果沒有類型轉換,則不能將Collection分配給List


這是我的代碼,供您一起玩。 (複製並粘貼到適當命名的文件中...)

public class Bar { 
} 

public class ChildBar extends Bar { 
} 

import java.util.*; 

public class Foo<T extends Bar> { 
    private Class<T> _type; 
    public Foo(Class<T> _type) { 
     this._type = _type; 
    } 
    public Collection<T> hypothetical(List<T> items) { 
     return items; // dummy implementation ... 
    } 
} 

import java.util.*; 

public class Main { 
    public static void main(String[] args) { 
     Foo<ChildBar> foo = new Foo<ChildBar>(ChildBar.class); 
     Collection<ChildBar> items = 
       foo.hypothetical(new ArrayList<ChildBar>()); 
    } 
} 
+0

Stephen C,感謝您將我的評論置於答案中。來自我的投票。 – Andreas

+0

@Stephen C,擅長嘗試完成代碼。原始代碼僅僅是一個代碼片段,只有足夠的信息來傳達帖子中的問題。可以合理推斷缺失的項目。請專注於解決**編譯器錯誤消息:** ***類型Foo 中的方法假設(List )不適用於參數(List )*** – 000

+0

編譯我的代碼版本時沒有編譯錯誤。沒有。不適用於Java7或Java 8.請仔細檢查我的代碼,並解釋代碼的不同之處,以便我們可以回答您的問題。 –

2

接受的答案並不能準確解釋編譯器拒絕問題片段(編輯後)的原因。

我們從觀察中發現@Stephen C的答案片段被接受,而問題修訂版8被拒絕。區別在於:在後一版本中,變量foo使用通配符參數化類型Foo<? extends ChildBar>進行聲明,而Stephen C從早期版本複製了Foo<ChildBar>(我們似乎都同意這是解決編譯錯誤的合適方法)。

要理解爲什麼這種差異是至關重要的,請參閱與Foo<? extends ChildBar> foo這個通配符傳播作爲捕捉到爲foo.hypothetical調用的簽名,所以這個調用呈現爲hypothetical(List<capture#2-of ?>),這意味着參數具有未知(上界)類型參數。 List<ChildBar>與該類型不兼容,因此編譯錯誤。

另請注意,在此線程中所有提及的「運行時」都是不恰當的,所有這些都是在編譯時靜態解決的。也許你的意思是調用類型或實際參數類型,而不是聲明類型(形式參數)。編譯器不知道實際的運行時類型。

+0

您的答案仍然支持接受答案中所述的相同前提。然而,你的回答遲到了,你的措詞是硫酸的。除了改寫已接受的答案並指出java在編譯期間解析其泛型類型參數之外,它沒有增加任何新知識。你當然可以在沒有好鬥的語言的情況下表達這些觀點。 – 000

+0

編譯期間解析運行時引用(以及它們的泛型類型的類型參數)。因此,在接受的答案中提到的「運行時參考」在其使用範圍內是完全合法的。 – 000

+1

@StephenIsienyi,具體而言:如果不提及通配符和捕獲的概念,則無法全面解釋情況。 –