二、泛型
Java5開始提供的新特性,表示不确定的類型。
注意:泛型是提供的javac編譯器使用的,它用于限定集合的輸入類型,讓
編譯器在源代碼級别上,擋住向集合中插入的非法資料。但編譯器編譯完之後,生産
的.class位元組碼檔案中不再代用泛型的資訊,依此不影響程式的運作效率,這個
過程被稱為“擦除”。
另外泛型還被用在方法或類上。
-------------------------------------
public class Demo2 {
public static List method1(){
//.....
return new ArrayList();
}
public static void method2(List list){
//...
public static void main(String[] args) {
List list1 = new ArrayList();//正确的
List<String> list2=new ArrayList<String>();//正确的
List<String> list3 = new ArrayList();//正确的
List<String> list3x = method1();//相容目前情況
List list4 = new ArrayList<String>();//正确的
List<String> list4x = new ArrayList<String>();
method2(list4x);//相容目前情況。
List<Object> list5 = new ArrayList<String>();//錯誤的
List<String> list6 = new ArrayList<Object>();//錯誤的
}
---------------------------
總結:可以兩邊都沒有;可以一邊有一邊沒有;
也可以兩邊都用,一旦兩邊都有的話,兩邊必須保持相同(不考慮邊界)。
2.2自定義泛型
分兩類:方法上的泛型和類上的泛型。
2.2.1方法上的泛型。
-------------------------
class Person{}
class Dog{}
public class God {
public Object kill(Object obj){
System.out.println("神弄死了:"+obj);
return obj;
public Person kill(Person p){
System.out.println("神弄死了的人是:"+p);
return p;
public Dog kill(Dog d){
System.out.println("神弄死了的狗是:"+d);
return d;
God god = new God();
Person p = god.kill(new Person());
Dog d = god.kill(new Dog());
------------------------------
神可以kill各種類型的對象,我們不可能為每一個類型都編寫一個方法,是以
需要使用定義在方法上的泛型。
注意:
(1).泛型需要先定義,再使用,方法上泛型需要定義在方法傳回值的前面,通常使用
一個大寫的英文字母(sun公司推薦使用T),也可以是任意的字母,但是不要使用
java中的關鍵字和常用的類(String)。
(2).定義在方法上的泛型的作用範圍是目前方法的内部。
-----------------
public <T> T kill(T t){
System.out.println("上帝弄死了"+t);
return t;
-------------------------
public <T> T save(T t){
System.out.println("上帝救活了:"+t);
如果把save方法上的<T>去掉,發現報錯,再次證明定義在方法
上的泛型隻用在目前方法的内部有效。
兩個方法上"T"不是同一個"T"。
-----------------------
God god = new God();
Person p = god.kill(new Person());
Dog d = god.save(new Dog());
如果想讓兩個方法上的T表示同一個,怎麼辦?
2.2.2.類上的泛型
定義在類上的泛型作用範圍是目前類的内部;
需要先定義再使用,類上的泛型定義在類名的後面。通常使用大寫的英文字母。
建立具有泛型的類(God)的對象時,通常需要指定泛型的具體類型。
如果在建立對象時,不明确指定泛型的具體類型,則預設為泛型的“上邊界”。
在類上定義的泛型不能用在靜态方法上,如果想在靜态方法上使用泛型,需要當該靜态
方法上重新定義。
另外檢視List接口源碼發現,它其實就是使用了定義在類上的泛型
public interface List<E> extends Collection<E>{
boolean add(E e);
void add(int index, E element);
E set(int index, E element);
<T> T[] toArray(T[] a);
}
-----------------------
想讓所有的神的對象之間公用泛型T:
public static <T> T sleep(T t){
System.out.println("神催眠了:"+t);
注意:在sleep方法上的T,和該方法所在類God<T>上的T不是同一個。
補充:在泛型使用中可以同時定義多個。
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{
public V put(K key, V value){...}
...
}
神馬是“上邊界”?
一、泛型進階
!!通配符 ?
因為泛型沒有繼承的概念,是以當需要用一個“泛型引用”引用不同的泛型實作時,泛型寫
他們的共同父類是不行的,這時該怎麼做?引入一個新的概念,叫做泛型的通配符“?”,注意
通配符隻能用在聲明處,不能用在實作執行個體的過程中。
void print(Collection<?> c){//Collection of unknown
for(Object obj:c){
System.out.pritnln(obj);
}
new ArrayList<Stdudent>();
new ArrayList<Person>();
注意:由于參數c類型為Collection<?>,表示一種不确定的類型,是以在方法内部
不能調用與類型相關的方法。
如果沒有指定泛型預設類型,可以接收任意類型,有時希望進一步限制範圍,需要使用泛型的邊界:
!!上邊界:限定通配符的上邊界
extends-用來指定泛型的上邊界,和泛型通配符配合使用(List<? extends Person> list),指定具體的泛型實作必須是指定類或它子孫類。
List<? extends Number> list1 = new Vector<Number>();//正确的
List<? extends Number> list2 = new Vector<Integer>();//正确的
List<? extends Number> list3 = new Vector<String>();//錯誤的
List<? extends Person> list1 = null;
list1 = new Vector<Teacher>();
Teacher thr = new Teahcer();
list1.add(thr);
SomeOne<T extends Person>
示範和相關總結見:cn.tedu.v1.Demo2
!下邊界:限定通配符的下邊界
super:用來指定泛型的下邊界,和通配符一起使用。指定具體的泛型實作必須是指定類或指定的“超類”
? super Person
這個原因也是很簡單的, 因為我們所傳入的類都是Integer的類或其父類, 所傳入的資料類型可能是Integer到Object之間的任何類型, 這是無法預料的, 也就無法接收. 唯一能确定的就是Object, 因為所有類型都是其子類型.
使用? super E還有個常見的場景就是TreeSet有這麼一個構造方法:
TreeSet(Comparator<? super E> comparator)
就是使用Comparator來建立TreeSet, 那麼請看下面的代碼:
public class Person {
private String name;
private int age;
public Person(){}
public Person(String name,int age){
this.name = name;
this.age = age;
}//省略getters和setters
public class Student extends Person {
public Student() {}
public Student(String name,int age) {
super(name,age);
class comparatorTest implements Comparator<Person>{
@Override
public int compare(Person p1, Person p2) {
int num = p1.getAge() - p2.getAge();
return num == 0 ? p1.getName().compareTo(p2.getName()):num;
}
public class Demo {
public static void main(String[] args) {
TreeSet<? super Person> ts1 = new TreeSet<Person>(new comparatorTest());
ts1.add(new Person("Tom", 20));
ts1.add(new Person("Jack", 25));
ts1.add(new Person("John", 22));
System.out.println(ts1);
TreeSet<? super Student> ts2 = new TreeSet<Student>(new comparatorTest());
ts2.add(new Student("Susan", 23));
ts2.add(new Student("Rose", 27));
ts2.add(new Student("Jane", 19));
System.out.println(ts2);
}
三、總結:
"in out"原則, 總的來說就是:
in就是你要讀取出資料以供随後使用(想象一下List的get), 這時使用extends關鍵字, 固
定上邊界的通配符. 你可以将該對象當做一個隻讀對象;
out就是你要将已有的資料寫入對象(想象一下List的add), 這時使用super關鍵字, 固定下
邊界的通配符. 你可以将該對象當做一個隻能寫入的對象;
當你希望in的資料能夠使用Object類中的方法通路時, 使用無邊界通配符;List<?>
當你需要一個既能讀又能寫的對象時, 就不要使用通配符了.