2015-07-04 47 views
5

問題是在一個通用的限制:是否可以使用簽名List <Class <?實現方法?在Java中擴展Annotation >>?

public List<Class<? extends Annotation>> getAnnotations() { 
    return new ArrayList<>(Arrays.asList(Override.class)); 
} 

實際收益類型爲ArrayList<Class<Override>>
方法需要List<Class<? extends Annotation>>

Class<Override>Class<? extends Annotation>
Class<? extends Annotation> c = Override.class; //允許

ArrayList是一個亞型子類型List,如果元素的類型匹配:
List<? extends Number> l = new ArrayList<Integer>(); //允許

然而,這是不允許的:

List<Class<? extends Annotation>> l = Arrays.asList(Override.class); 
List<Class<? extends Annotation>> l = new ArrayList<>(Arrays.asList(Override.class)); 

它甚至有可能還是Class通配符被打破?

+0

@kocko你使用了什麼java?我有jdk1.7_079,這裏有一個截圖https://www.dropbox.com/s/p6gybp1jct19ehg/generics.jpg – AdamSkywalker

+1

我玩過這個,我想我找到了原因。我會在一段時間後寫一個答案。 –

+1

從測試中,看起來像在Java 7中,您需要參數化靜態調用'Arrays。> asList(Override.class);'。對於Java 8,我不需要 –

回答

2

我會認爲這是因爲jdk 1.7類型的推理性質。

正如你可能已經知道,Arrays.asList(T ... elems)方法是通用的,但我們很少明確地指定我們想與之合作的方法類型參數,因此,我們編譯器的類型推理功能依賴

所以,當編譯器看到Arrays.asList(Override.class)聲明將推斷,對於方法的類型參數應與Class<Override>被替換,即我們不得不在這種形式的方法的版本:

public List<Class<Override>> asList(Class<Override> ... elems) 

但是,如果明確設置該方法的類型參數

List<Class<? extends Annotation>> l = 
       Arrays.<Class<? extends Annotation>>asList(Override.class); 

那麼編譯器會真正知道什麼類型的參數必須與被替代之後,.asList()方法的版本將是:

public List<? extends Annotation> asList(Class<? extends Annotation> ... elems) 

現在,這將編譯很好,因爲Class<? extends Annotation>兼容到Class<Override>。在Java8中,類型推斷功能得到了進一步改進,因此您不必爲.asList()方法明確設置類型參數。

然而,有趣的問題去

爲什麼List<Class<Override>>是不兼容List<Class<? extends Annotation>>

java.lang.Classfinal之一,這將有助於回答以下兩個問題,結合其將回答上述問題。:)

所以,

  • 什麼是一個List<Class<Override>>是什麼意思?

List<Class<Override>>意味着我們只能添加Class<Override>的實例,而沒有其他任何內容添加到列表中。這很好,知道我們甚至不能添加Class<Override>子類,因爲Class類型是final

  • List<Class<? extends Annotation>>是什麼意思?

這種類型的List代表的類別列表的整體家庭,所有這一切都是Annotation型,這意味着我們能夠成功地添加任何註釋類型(例如,SuppressWarnings.classOverride.class的子類,Documented.class等)到列表中。

讓我們假設下面的例子實際上是正確的:

List<Class<Override>> overrides = Arrays.asList(Override.class); 
List<Class<? extends Annotation>> annotations = new ArrayList<>(); 
annotations = overrides; 
annotations.add(SuppressWarnings.class); //HUGE PROBLEM 
annotations.add(Documented.class); //ANOTHER HUGE PROBLEM 

兩個巨大的問題來自於一個事實,即我們正在努力一些非Override實例添加到overrides,這是非常錯誤的。

我們有足夠智能的編譯器,可以實際檢測到這些可能的問題,並且拋出編譯時錯誤是阻止我們這樣做的方式。

更多信息:

+0

感謝您花費的時間。第一部分告訴我們我們剛剛被類型檢測欺騙了。據我所知,Java8中有一個改變,它分析了分配語句的左邊部分,所以它解決了這個問題。第二部分提到了一個典型的子集類問題,我很熟悉這個問題。讓我們等待幾天,看看我們是否遺漏了一些東西,如果一切正常,我會接受。再次感謝:) – AdamSkywalker

+0

不客氣。 :) –

1

ArrayList的是一個列表的一個子類型,如果類型的元素的匹配:

List<? extends Number> l = new ArrayList<Integer>(); // allowed 

是的,但在你的例子元素類型不匹配:

List<Class<? extends Annotation>> l = new ArrayList<Class<Override>>(); 

誠然,Class<Override>Class<? extends Annotation>一個亞型,但就像List<String>不是List<Object>一個亞型,List<Class<Override>>不是List<Class<? extends Annotation>>一個亞型。不過,這將是List<? extends Class<? extends Annotation>>的子類型。

也就是說,你的代碼無法編譯的原因是,在推斷返回語句表達式的類型時,類型推斷在Java 7中沒有考慮方法的返回類型,所以它默認爲最具體的類型能分到

Arrays.asList(Override.class) 

沒有意識到return語句只會有一個更靈活的類型編譯(8的Java類型推斷是聰明的,順便說一句)。一個解決辦法是顯式地指定類型參數:

Arrays.<Class<? extends Annotation>(Override.class); 

或給Java 7中的類型推斷將其分配給局部變量第一個提示:

List<Class<? extends Annotation>> list = Arrays.asList(Override.class); 
return list; 

或更改方法返回類型

List<? extends Class<? extends Annotation>> getAnnotations() 

所以推斷的類型並不重要。

+0

最後的代碼行看起來很可笑,但工程,哈哈 – AdamSkywalker

相關問題