2014-11-01 50 views
1

this answer我試圖創建一個靜態實用方法,使一個ListMap通用靜態方法限制類型太多

public static <K, T> Map<K, T> toMapBy(List<T> list, 
     Function<? super T, ? extends K> mapper) { 
    return list.stream().collect(Collectors.toMap(mapper, Function.identity())); 
} 

它工作得很好。但是,我發現該方法不能與list.stream().collect(...)表達式在所有相同的上下文中使用。該方法不夠靈活。

List<Student> students = Arrays.asList(); 

Map<Long, Student> studentsById1 = students.stream() 
     .collect(Collectors.toMap(Student::getId, Function.identity())); 
Map<Long, Student> studentsById2 = toMapBy(students, Student::getId); 

Map<Long, Person> peopleById1 = students.stream() 
     .collect(Collectors.toMap(Student::getId, Function.identity())); 
Map<Long, Person> peopleById2 = toMapBy(students, Student::getId); // compile error! 

在這個例子中,是StudentPerson亞型和具有getId方法,它返回一個Long

最後一條語句失敗,出現incompatible types: inference variable T has incompatible bounds ...(JDK 1.8.0_25)。有沒有一種方法來定義類型參數,以便靜態方法與其包含的表達式在相同的上下文中工作?

+0

[is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-polymorphic?](http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-的-listanimal - 爲什麼 - ARENT-Java類,泛型隱-p)。 'Map '不能被引用到返回的'Map '。也許考慮使用'Map '。 – Pshemo 2014-11-01 18:05:32

+0

如果你可以使用'Map 'Map '的實例,那麼這意味着通過這樣的引用你可以在這張地圖上放置任何類型的人,不僅是學生(換句話說泛型不是協變的)。 – Pshemo 2014-11-01 18:15:04

回答

6

你可以添加一個類型參數爲地圖的值,以便它們可以從T爲不同:

public static <K, V, T extends V> Map<K, V> toMapBy(List<T> list, 
     Function<? super T, ? extends K> mapper) { 
    return list.stream().collect(Collectors.toMap(mapper, Function.identity())); 
} 
1

最後一行調用方法toMapBy,其中編譯器推斷Student的類型爲T。所以它顯然返回List<Long, Student>

But generics aren't covariant!

這意味着,你不能分配給List<Long, Student>List<Long, Person>類型的變量,因爲它們不是在一個亞型的關係。

的解決方案是使用子形式:

Map<Long, ? extends Person> peopleById2 = toMapBy(students, Student::getId); // no compiler error 
+1

是的,但問題是是否可以定義'toMapBy'的類型參數,以便它可以在與流表達式相同的上下文中工作。換句話說,爲什麼'Map '是倒數第二行中的賦值目標,而不是最後一行? – glts 2014-11-01 18:15:14

+0

有趣。但看看@Alex'的答案。這是解決方案。 – Seelenvirtuose 2014-11-01 18:29:49

0

利用該位:

Map<Long, Person> peopleById1 = students.stream() 
     .collect(Collectors.toMap(Student::getId, Function.identity())); 

請注意,您不提供參數給Function.identity()。編譯器可以自由地將其推斷爲Function.<Person>identity()來解決返回值賦值造成的差異。

這應該是你的目的不夠好:

public static <K, T> Map<K, T> toMapBy(
    List<? extends T> list, // <- note 
    Function<? super T, ? extends K> mapper 
) { 
    ... 
} 

現在列表的元素可以是地圖值的亞型。或者您可以定義像@Alex建議的第三個參數。