[Java] 14.1. 泛型 & 集合

泛型,確保集合中裝下的物件類型。

 

image/svg+xml14.1. 泛型 & 集合 – Generic & Collections 泛型與集合總整理 class Person { String id, name, gender; int age; } class Employee extends Person{ String employeeID; int salary; Employee( String id, String name, int age , String gender, String employeeID, int salary){ this .id = id; this .name = name; this .age = age; this .gender = gender; this .employeeID = employeeID; this .salary = salary; } double getBonus (){ return 1 *salary; } } class Cat { String name; Cat( String name){ this .name = name;} } class Dog { String name; Dog( String name){ this .name = name;} } import java.util.*; class Main{ public static void main( String [] args){ ArrayList schedule = new ArrayList (); Employee e1 = new Employee ( "K12345678" , “Jack" , 20 , " 男⽣ " , "048679" , 40000 ); Employee e2 = new Employee ( "K00000000" , “Eric" , 35 , " 男⽣ " , "044000" , 50000 ); schedule.add ( e1 ); schedule.add ( e2 ); Cat c = new Cat ( "Cookie" ); Dog d = new Dog ( "Coopy" ); schedule.add ( c ); schedule.add ( d ); } } import java.util.*; class Main{ public static void main( String [] args){ ArrayList < Employee > schedule = new ArrayList < Employee >(); Employee e1 = new Employee ( "K12345678" , “Jack" , 20 , " 男⽣ " , "048679" , 40000 ); Employee e2 = new Employee ( "K00000000" , “Eric" , 35 , " 男⽣ " , "044000" , 50000 ); schedule.add ( e1 ); schedule.add ( e2 ); for ( Employee e : schedule ){ System.out.println( e.name ); System.out.println( e.salary ); } } } 執⾏結果 Jack 40000 Eric 50000 import java.util.*; class Main{ public static void main( String [] args){ ArrayList < Employee > schedule = new ArrayList < Employee >(); Employee e1 = new Employee ( "K12345678" , “Jack" , 20 , " 男⽣ " , "048679" , 40000 ); Employee e2 = new Employee ( "K00000000" , “Eric" , 35 , " 男⽣ " , "044000" , 50000 ); schedule.add ( e1 ); schedule.add ( e2 ); Cat c = new Cat ( "Cookie" ); schedule.add ( c ); } } 執⾏結果 JMain.java:12: error: no suitable method found for add(Cat) schedule.add(c); ^ method Collection.add(Employee) is not applicable (argument mismatch; Cat cannot be converted to Employee) method List.add(Employee) is not applicable (argument mismatch; Cat cannot be converted to Employee) method AbstractCollection.add(Employee) is not applicable (argument mismatch; Cat cannot be converted to Employee) method AbstractList.add(Employee) is not applicable (argument mismatch; Cat cannot be converted to Employee) method ArrayList.add(Employee) is not applicable (argument mismatch; Cat cannot be converted to Employee) Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output 1 error class Programmer extends Employee { Programmer( String id, String name, int age , String gender, String employeeID, int salary){ super (id, name, age, gender, employeeID, salary); } void code(){ System.out.println( " " + name + " 寫了⼀⾏程式 " ); } void debug(){ System.out.println( " " + name + " 除去⼀個 Bug" ); } double getBonus(){ return 4 *salary;} } class Tester extends Employee { Tester( String id, String name, int age, String gender , String employeeID, int salary){ super (id, name, age, gender, employeeID, salary); } void test(){ System.out.println( " " + name + " 測試完⼀個系統 " ); } double getBonus(){ return 1.5 *salary;} } class Artist extends Employee { Artist( String id, String name, int age, String gender , String employeeID, int salary){ super (id, name, age, gender, employeeID, salary); } void draw(){ System.out.println( " " + name + " 畫完⼀幅畫 " ); } double getBonus(){ return 3 *salary;} } import java.util.*; class Main{ public static void main( String [] args){ ArrayList < Employee > schedule = new ArrayList < Employee >(); Programmer e1 = new Programmer ( "K12345678" , "Jack" , 20 , " 男⽣ " , "048679" , 40000 ); Tester e2 = new Tester ( "K00000000" , "Eric" , 35 , " 男⽣ " , "044000" , 50000 ); Artist e3 = new Artist ( "K00000000" , "Marry" , 23 , " 女⽣ " , "045000" , 35000 ); schedule.add ( e1 ); schedule.add ( e2 ); schedule.add ( e3 ); for ( Employee e : schedule){ if ( e instanceof Programmer ) ((Programmer)e).code(); if ( e instanceof Tester ) ((Tester)e).test(); if ( e instanceof Artist ) ((Artist)e).draw(); } } } import java.util.*; class Main{ public static void main( String [] args){ ArrayList < Object > schedule = new ArrayList < Employee >(); schedule.add ( new Employee ()); schedule.add ( new Dog ()); } } import java.util.*; class Main{ public static void main( String [] args){ ArrayList < Employee > schedule = new ArrayList < Employee >(); List < Employee > l = schedule ; } } class Employee extends Person implements Comparable < Employee >{ String employeeID; int salary; // 略過⼀些程式 public int compareTo ( Employee e ) { if ( this .salary > e.salary) return 1 ; if ( this .salary < e.salary) return - 1 ; return 0 ; } } import java.util.*; class NameComparator implements Comparator < Employee >{ public int compare ( Employee e1 , Employee e2 ){ return e1 . name.compareTo ( e2 . name ); } } import java.util.*; class AgeComparator implements Comparator < Employee >{ public int compare ( Employee e1 , Employee e2 ){ if (e1.age > e2.age) return 1 ; if (e1.age < e2.age) return - 1 ; return 0 ; } } J14_1_1 Person.java J14_1_1 Employee.java J14_1_1 Cat.java J14_1_1 Dog.java J14_1_1 Main.java 1. 這是⼈員類別( Person )。 2. 這是員⼯類別( Employee )。 3. 取得獎⾦⽅法( getBonus() ),先加上,後⾯會使⽤到。 4. 這是阿貓類別( Cat )。 5. 這是阿狗類別( Dog )。 6. 建立 ArrayList 物件,以模擬班表。 7. 建立並加入兩個員⼯物件,正常~~ 8. 怎麼連阿貓阿狗都排進班表中了? J14_1_2 Main.java 1. 使⽤⼀對箭號( <> )來使⽤泛型,⽽箭號中放的就型別,⽤以限制 List 集合中的物件型別。記得變數的泛型型別必須與物件的泛型型別⼀致。 2. 沒問題, e1 e2 變數中的物件,其型別是員⼯型別 Employee )。 Schedule 變數中的集合不會拒絕員⼯物件的進入。 3. 即使是使⽤列舉的⽅式取出員⼯物件,皆不需要再進⾏強 制轉型的動作。因為泛型以指定了放在集合中的型別就是員⼯ 型別的物件,所以拿出來的資料也會是員⼯型別的物件。 4. 列舉取得的資料。您也可以使⽤ get() ⽅法取出資料,當然取出 的資料直接就是員⼯型別的物件,不需要在進⾏強制轉型的動作。 J14_1_3 Main.java 1. 指定泛型為員⼯類別( Employee )。 2. 沒問題,加入 2 個員⼯物件。 3. 建立阿貓物件( Cat )。 4. 若嘗試將不是員⼯類別的物件(例如:貓或狗類別的物件)放入到 List 合中,編譯器會很聰明的告訴您,並參數為 Cat 型別的 add() ⽅法可使⽤,只有 參數為 Employee 型別的 add() ⽅法可使⽤。這說明了泛型是編譯時期進⾏型別 檢查的。這樣就可以保證放入⾄班表 List 中的物件,⼀定就是員⼯物件。 J14_1_4 Main.java J14_1_4 Programmer.java J14_1_4 Tester.java J14_1_4 Ar @ st.java 1. 程式設計師類別( Programmer ),繼承⾃員⼯類別( Employee )。 2. 測試⼈員類別( Tester ),繼承⾃員⼯類別。 3. 美⼯⼈員類別( Artist ),繼承⾃員⼯類別。 3. 指定泛型為員⼯類別( Employee )。 5. 建立不同⾓⾊的員⼯類別。 6. e1 變數中放的是程式設計師型別( Programmer )的物 件。但沒關係,⾃動轉型為員⼯型別( Employee )的物 件,然後就可以放入泛型為員⼯類別的班表 List 集合中了。 8. e3 變數中放的是美⼯⼈員型別( Tester )的物件。 但沒關係,⾃動轉型為員⼯型別( Employee )的物件, 然後就可以放入泛型為員⼯類別的班表 List 集合中了。 7. e2 變數中放的是測試⼈員型別( Tester )的物件。但沒 關係,⾃動轉型為員⼯型別( Employee )的物件,然後就可 以放入泛型為員⼯類別的班表 List 集合中了。 9. 當然泛型後集合,最多只能夠以指定的泛型將物件取出來,因此 雖然不同⾓⾊的員⼯物件可以透過⾃動轉型的⽅式放入指定泛型的集 合中,但在取出時還是需要強制轉型才可還原成原物件的型別的。 J14_1_5 Main.java 1. schedule 變數即被裝入集合物件, 然後此變數來操作集合物件。 2. 這是變數上的泛型。 3. 這是集合物件上的泛型。 . 這是決對不充許的,變數上泛型的型別與物 件上泛型的型別,這⾏程式是會發⽣編譯錯誤 的。雖然這看起來泛型在進⾏多型的應⽤。 6. 沒問題!員⼯物件可以放入集合物件中。操作集合物件 schedule 變數,它的泛型型別是 Object ,⽽員⼯物件 也是擁有 Object 型別,因此可以在⾃動轉型後放入。 5. 以下的程式是不可能執 ⾏的。但我們假設這些程式 是可以的,來看⼀下若我們 將集合物件上的泛型與變數 上的泛型設成不⼀致的情況 下,程式將如何打破泛型保 證物件型別⼀致的限制。 7. 問題來了,操作集合物件的 schedule 變數,它的泛型 型別指出它可以放入 Object 型別的物件,但 schedule 數操作的集合物件上的泛型,卻指出只有是員⼯物件才可以 放入集合中。如此就打破了泛型保證型別⼀致的限制了。 J14_1_6 Main.java 1. 基底型別指的就是⽀援泛型型別的類別型別。 2. schedule 變數中,存放了 ArrayList 的物件,⽽ ArrayList 是實作 List 介⾯的類別,因此可以將 ArrayList 的物件直接轉型成 List 型別的物件。 schedule 的泛型型別 - 員⼯類別( Employee )並不會影響基底類別 - ArrayList 進⾏轉型成 List 的動作,因為在 List 上的泛型型別也同樣是員⼯類 別,因此並不會破壞泛型確保集合物件的型別必須是員⼯類別的規則。 ) ( : ) : ) : ) : : ) : : ) ) < : < : : ) : ) : ) : ) : ) > ) . > > < : > : > . ) > : : : > . . . > : : , > , > . ( > < ) . : > : > < . > < . , , 1. Collection<E> 介⾯加上泛型參數 <E> 因此繼承⾃ Collection<E> 也將⽀援泛型。 這也造成後續實作的類別也必須⽀湲泛型。 2. 可搭配 for 使⽤的列舉介⾯ Iterator<E> ⽀援泛型,因此當集合透過 iterator() ⽅法取得 列舉器時,將會透過泛型代入列舉的物件型別。 3. 比較特別的是 Map<K,V> 介⾯,因 Map 在放入物件時需要同時給予 key value 兩個 物件,因此 Map<K,V> 使⽤ 2 個泛型參數, K 表⽰ key 的型別, V 表⽰ value 的型別。 J14_1_7 Main.java 1. 這是員⼯類別( Employee )。 2. 實作⽀援泛型的 Comparable<E> 介⾯,並以員⼯類別作為 泛型的型別,此時在實作 CompareTo() ⽅法時,讓員⼯物件有 比較⼤⼩的能⼒,同時參數的型別也由 Object 改成員⼯類別, 這也是因為泛型的關係,指定了參數的型別必須是卸型的型別。 3. 以薪資作為比較員⼯物件⼤⼩的條件。 ⼤於時回傳正數 1 ;⼩於時回傳負數 -1 4. 不⼤於也不⼩於時,那就是等於了,所以回傳 0 J14_1_8 NameComparator.java J14_1_8 AgeComparator.java 1. 建立 1 個新的類別,取名為 NameComparator 表⽰這是員⼯姓 -Name 的排序比較器,實作的是⽀援泛型的 Comparator<E> ⾯,泛型代入的是比較物件的型別員⼯類別( Employee )。 2. 改寫 compare() ⽅法,並將排序的規則寫在此⽅法中,此時代 入的 2 個參數型別皆是泛型所代入的型別員⼯類別( Employee )。 3. 因姓名屬性的型別是 String ,它是⼀個類別,同時也實作了 comparable 介⾯,因此直接使⽤ String compareTo() ⽅法進⾏姓名的 比⼤⼩的動作。當然最後也是回傳整數,以代表⼤於、⼩於或等於的關係。 4. 這是另 1 個排序比較器,取名為 AgeComparator 表⽰這是以 年齡作為排序準則的。實作的是⽀援泛型的 Comparator<E> ⾯,泛型代入的是比較物件的型別員⼯類別( Employee )。 5. 改寫 compare() ⽅法,並將排序的規則寫在此⽅法中,此時代 入的 2 個參數型別皆是泛型所代入的型別員⼯類別( Employee )。 6. 比較 2 個員⼯物件的年齡( age ),⼤於回傳 1 ,⼩於回傳 -1 ,等於回傳 0 您的班表 List ,即便是阿貓阿狗也是可以加入? 泛型,保證放入班表 List 中的⼀定是員⼯物件 1 1 將非員⼯的物件放入班表時,編譯器會產⽣錯誤 1 1 不同⾓⾊的員⼯也是可以放入班表中的 注意, 請確保班表變數上的泛型與班表物件上的泛型是否⼀致 其實泛型並不會限制基底類別的轉型, 只會限定可以放入集合中的物件型別 Comparable<E> 介⾯, 輕鬆完成比較薪⽔⼤⼩的功能 Comparator<E> 介⾯,讓員⼯物件輕鬆擁有更多的排序外掛

留言