很多使用泛型的小伙伴,都會有一個疑惑:為什麼有的方法返回值前帶<T>、<K, V>之類的標記,而有的方法返回值前又什麼都不帶呢?就像這樣: // 實體基類 class Entity { public String toString() { return "Entity"; } } // 用戶類 cl ...
很多使用泛型的小伙伴,都會有一個疑惑:為什麼有的方法返回值前帶<T>、<K, V>之類的標記,而有的方法返回值前又什麼都不帶呢?就像這樣:
// 實體基類 class Entity { public String toString() { return "Entity"; } } // 用戶類 class User extends Entity { public String toString() { return "User"; } } // 用戶Dao class UserDao { public String toString() { return "UserDao"; } } /** * 帶<E>標記與不帶<E>標記的比較 */ public class GenericsClass<T extends Entity> { // 不帶標記 public void notSign(T t) { System.out.println("notSign " + t.toString()); } // 帶<T>標記 public <T> void hasSign(T e) { System.out.println("hasSign " + e.toString()); } public static void main(String[] args) { GenericsClass<Entity> clazz = new GenericsClass<>(); Entity entity = new Entity(); User user = new User(); UserDao userDao = new UserDao(); System.out.println("不帶標記的方法:"); clazz.notSign(entity); clazz.notSign(user); // 不能編譯通過的 // 因為在GenericsClass<T extends Entity>中已經限定了全局的T為Entity及其子類, // 所以不能再加入UserDao; // clazz.notSign(userDao); System.out.println("帶標記的方法:"); clazz.hasSign(entity); clazz.hasSign(user); // 帶上首碼<E>,就是在告訴編譯器:這是新指定的一個類型,代表該方法自己獨有的某個類, // 跟GenericsClass<T extends Entity>中的Entity及其子類沒有任何關係 // 或者說 // hasSign方法重新定義泛型T、隱藏或者代替了GenericsClass<T>中的T,不再受限於Entity及其子類 clazz.hasSign(userDao); } }
因此,返回值前面的<T>的作用是「重新定義泛型」,因此方法參數類型不受對象泛型類型的限制。在Java新的Stream API中有大量這種帶首碼的用法,例如:
ArrayList.java:public <T> T[] toArray(T[] a) Optional.java:public static <T> Optional<T> of(T value) Collectors.java:public static <T> Collector<T, ?, List<T>> toList()
泛型,作為Java的一個基礎特性,並不是一點毛病都沒有,「泛型擦除」就是至今還未解決的一個問題(這個問題其實對於大多數人來說可以不用知道,因為實際應用中極少出現這種場景,感興趣的話稍稍瞭解一下,不喜可繞過)。
所謂泛型擦除,是這樣一個問題:
class User {} class Product {} class Shop<V> {} class Particle<PPP, QQQ> {} public class LostInformation<T> { public static void main(String[] args) { // ArrayList<String>和ArrayList<Integer>應該是不同的類型,但結果是它們完全相等 Class<?> c1 = new ArrayList<String>().getClass(); Class<?> c2 = new ArrayList<Integer>().getClass(); System.out.println(c1 == c2); // 無法從泛型獲得任何有關參數類型的信息 List<User> list = new ArrayList<>(); Map<User, Product> map = new HashMap<>(); Shop<User> shop = new Shop<>(); Particle<Long, Double> p = new Particle<>(); System.out.println(Arrays.toString(list.getClass().getTypeParameters())); System.out.println(Arrays.toString(map.getClass().getTypeParameters())); System.out.println(Arrays.toString(shop.getClass().getTypeParameters())); System.out.println(Arrays.toString(p.getClass().getTypeParameters())); } }
可以看見,Particle<Long, Double>中關於Long和Double的信息已經完全丟失了,只剩下了最初的PPP和QQQ,真的應了那句話:歷經千帆,歸來仍是是少年!
但這是編程啊,不是文案啊~,不能這樣丟掉了!