到底 <T extends Comparable<? super T>>是什麼意思
December 23, 2018每次看到聲明中有
<T extends Comparable<? super T>>
都會想辦法研究一下 搞懂之後 過幾天又忘記了 為了減少下次複習的時間 也為了分享學習的心得 產生了這篇文章 而且我也很有信心這篇會是網路上講解這個聲明最清楚的文章
希望讀者可以先看過Item31之後 再來讀這篇
前情提要
以下的聲明 大家都看得懂
<T extends Comparable<T>>
限制是說 T必須要實作Comparable<T>(只有這樣 T之間才能互相比大小) 比如具體類T是Student 那它必須 implements Comparable<Student>
<T extends Comparable<? super T>>
限制是說 T必須要實作Comparable<T或是T的任意父類>(只有這樣 T的實例之間 或是T和他的父類的實例之間 才能互相比大小)
以上的定義取自Item31
但我相信各位看官根本有看沒有懂 這篇文章舉個實際例子 來讓你真正通透
Welcome to the jungle
先來各自定義一下兩種不同的sort
public static <T extends Comparable<T>> void mySort1(List<T> list)
{
Collections.sort(list);
for(T item: list) System.out.println(item);
}
public static <T extends Comparable<? super T>> void mySort2(List<T> list)
{
Collections.sort(list);
for(T item: list) System.out.println(item);
}
定義一下我們的類別 Person是父類 Student是子類 藉由年紀排序
class Person implements Comparable<Person>
{
protected int age;
public Person(int age)
{
this.age = age;
}
@Override
public int compareTo(Person other)
{
return this.age - other.age;
}
}
class Student extends Person
{
public Student(int age) {
super(age);
}
}
建立一下我們的側資
List<Person> allPersons = new ArrayList<Person>();
allPersons.add(new Person(10));
allPersons.add(new Person(20));
List<Person> mixedPerson = new ArrayList<Person>();
mixedPerson.add(new Person(30));
mixedPerson.add(new Student(40));
List<Student> allStudent = new ArrayList<Student>();
allStudent.add(new Student(5));
allStudent.add(new Student(18));
好戲登場
對於這三組測資 猜猜哪些可以排序哪些不行
mySort1(allPersons); // 1
mySort1(mixedPerson); // 2
mySort1(allStudent); // 3
mySort2(allPersons); // 4
mySort2(mixedPerson); // 5
mySort2(allStudent); // 6
給你一分鐘 一分鐘後公佈答案
相信我 請認真的看一下mySort1跟mySort2的聲明 認真的想一下 這樣等一下看解釋的時候會學到最多 我不會害你
揭曉答案
答案是 第三個無法執行 且聽我娓娓道來
先看mySort1
public static <T extends Comparable<T>> void mySort1(List<T> list)
如果輸入是List of T 那裡面的每個T都必須要有實作Comparable<T>
1.allPersons: T在這裡是Person 不用多說 Person有實作Comparable<Person> 當然可以跑
2.mixedPerson: T在這裡是Person 輸入參數中有些Person 有些Student 問題在於 Student有沒有實作Comparable<Person>呢 答案是有 因為繼承自Person
3.allStudent: T在這裡是Student Student有沒有實作Comparable<Student>呢 答案是沒有 Student只有實作Comparable<Person> 所以不能執行 compile-time爆錯
再看mySort2
public static <T extends Comparable<? super T>> void mySort2(List<T> list)
如果輸入是List of T 那裡面的每個T都必須要有實作Comparable<T>
或是Comparable<T的父類>
1.allPersons: T在這裡是Person 不用多說 Person有實作Comparable<Person> 當然可以跑
2.mixedPerson: T在這裡是Person 輸入參數中有些Person 有些Student 問題在於 Student有沒有實作Comparable<Person>呢 答案是有 因為繼承自Person
3.allStudent: T在這裡是Student Student有沒有實作Comparable<Student>或是Comparable<Student的父類>呢 答案是有 Student有繼承Comparable<Person> 所以可以執行
老師我有問題
Q1.老師你偷懶 你的Student在繼承Person的同時 也同時實作Comparable<Student>不就好了嗎
A1.答案是不行 一個類別不能同時實作兩個不同類型的同個介面
你的Student不能同時實作Comparable<Student> 又同時有Comparable<Person>
Q2.不管啦 我就是要實作Comparable<Student>
A2.那你剩兩個選擇
第一個方法是Person也有實作Comparable<Person>然後Student不繼承Person 相信這不構成選項 Student沒繼承Person就是兩個完全不相關的類別 mixedPerson也無法創建
第二個選項 Person不實作Comparable<Person>
class Person
{
protected int age;
public Person(int age)
{
this.age = age;
}
}
class Student extends Person implements Comparable<Student>
{
public Student(int age) {
super(age);
}
@Override
public int compareTo(Student other)
{
return this.age - other.age;
}
}
mySort1(allPersons); // 1
mySort1(mixedPerson); // 2
mySort1(allStudent); // 3
mySort2(allPersons); // 4
mySort2(mixedPerson); // 5
mySort2(allStudent); // 6
那這六個只剩3跟6可以 因為根本沒有人實作Comparable<Person>
結論
現在知道為什麼Comparable<? super T>
這麼重要了吧 因為一個類別不能同時實作兩個不同類型的同個介面
所以最好一個List中的所有人都繼承自最父類的Comparable
這麼一來Comparable<? super T>
這個宣告 就可以在類型安全的前提下 讓你的API最廣泛地被客戶使用
看看Oracle的官方文件Collection.sort 或是其他支援泛型的函式 都是這樣宣告的 相信看到這裡你已經知其然也知其所以然