前一陣子在寫DiscordBot的時候,曾經在教學中看過這種寫法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| public class GymBotConfiguration {
@Bean
public <T extends Event>GatewayDiscordClient gatewayDiscordClient(final List<EventListener<T>> eventListeners){
final GatewayDiscordClient client = DiscordClientBuilder.create(token)
.build()
.login()
.block();
for (EventListener<T> listener : eventListeners) {
client.on(listener.getEventType())
.flatMap(listener::execute)
.onErrorResume(listener::handleError)
.subscribe();
}
return client;
}
}
|
對於其中的寫法感到非常困惑,不知道為什麼回傳值前還會有泛型符號,直到最近在研究一些OpenSource的框架時,才又更了解這件事情。
如何宣告泛型
在講這件事情之前,要先有個基礎知識,就是關於Java是如何宣告一個泛型Class的,其實就是在Class後面加上<T>就可以了
1
2
3
| public class Link<E> {
}
|
如此一來,就可以在其他地方創建一個泛型的Link類
1
| Link link=new Link<Integer>;
|
現在我們在這個Link類裡新增一個方法
1
2
3
4
5
6
7
| public class Link<E> {
public List<E> addToList(E t){
List<E> list = new ArrayList<>();
list.add(t);
return list;
}
}
|
這個方法會把傳進來的值,包裝成一個ArrayList並回傳出去,而它參數的型別則是與Link的泛型一致,比如說
1
2
3
4
| public static main(String[] arg){
Link link=new Link<String>;
List<String> list=link.add("早安");
}
|
也就是說addToList這個方法的型別和Link的泛型型別是綁定在一起的。
何謂回傳型別前的泛型
承上,因為方法的參數型別與類的泛型型別基本上綁定在一起,如果我宣告了一個Link<Integer>,我的addToList就只能吃Integer的參數,那為了要解決這種問題,就有了在回傳型別前的泛型的寫法,釋例如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| public class Link<E> {
public List<E> addToList(E t){
List<E> list = new ArrayList<>();
list.add(t);
return list;
}
public<E>List<E> addToList2(E t){
List<E> list = new ArrayList<>();
list.add(t);
return list;
}
}
|
可以看到addToList2這個方法的回傳值List<E>前,我加了一個<E>,先別管這個到底能做什麼,我們現在先使用這個方法看看
這時候你發現了,addToList2能吃的參數變成Object類了,當我們輸入一個String
輸入一個Double
也可以輸入一個自定義的類
可以感受到addToList跟addToList2的差異了嗎?沒錯,addToList2的泛型不受Link的影響,可以自己有自己的泛型
應用
這樣的用法不只可以拿來用在泛型類裡的方法,也可以用在一個非泛型類,比如說
1
2
3
4
5
6
7
| public class Student {
public List<Student> addPeopleList(Student student){
ArrayList<Student> list = new ArrayList<>();
list.add(student);
return list;
}
}
|
今天我們希望這個addPeopleList的方法可以吃任何型別的參數,我們如果這樣寫是會報錯的
原因就在於我們並沒有一個地方告訴編譯器,什麼是T,因為我們的Student類並不是一個泛型類,但實務上,我們又不太可能去修改原有的類,避免破壞掉一些東西,所以我們修改addPeopleList,讓它變成這樣
如此一來,addPeopleList就可以吃任何型別的參數了,以上就是所謂的泛型方法約束。