600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > Java基础 --- 泛型 Generics

Java基础 --- 泛型 Generics

时间:2022-03-30 04:37:56

相关推荐

Java基础 --- 泛型 Generics

Java基础 --- 泛型 Generics

为什么需要泛型泛型Bounds for Type VariableJava虚拟机如何处理泛型 --- 泛型擦除Restrictions and Limitations通配符类型 --- Wildcard Types

为什么需要泛型

在没有泛型类之前, Java用继承解决泛型问题. 也就是利用Object类

// before generic classes public class ArrayList {private Object[] elementData. . .public Object get(int i) {. . . }public void add(Object o) {. . . }}

这样有两个问题
当获取值时必须要转型

ArrayList files = new ArrayList();

String filename = (String) files.get(0);没有error checking, 可以给这个arraylist加入任何类(因为所有类的父类都是object)

files.add(new File(". . ."));This call compiles and runs without error. Elsewhere, casting the result of get to a String will cause an error

泛型提供了type parameter, 这样就可以给泛型指定一个类型, 代码更易读, 不用转型并且有error checking

ArrayList<String> files = new ArrayList<String>();

泛型

泛型类

public class Pair<T> {private T first;private T second;public Pair() {first = null; second = null; }public Pair(T first, T second) {this.first = first; this.second = second; }public T getFirst() {return first; }public T getSecond() {return second; }public void setFirst(T newValue) {first = newValue; }public void setSecond(T newValue) {second = newValue; }}

泛型方法

class ArrayAlg {public static <T> T getMiddle(T... a) {return a[a.length / 2];}}

Bounds for Type Variable

class ArrayAlg {public static <T> T min(T[] a) // almost correct {if (a == null || a.length == 0) return null;T smallest = a[0];for (int i = 1; i < a.length; i++)if (pareTo(a[i]) > 0) smallest = a[i];return smallest;}}

上面的代码, T可以是任何类, 但是.compareTo方法只有实现了Comparable接口才有如何确保 T 一定实现了Comparable接口
可以给type variable限定bounds, 也就是限定T必须继承某个父类或者接口

public static <T extends Comparable> T min(T[] a)

也可以限定多个接口T extends Comparable & Serializable虽然bound可以是接口也可以是类, 但是最多只能限定一个类, 并且必须放在bounds list的第一个. 接口可以限定多个

class ArrayAlg{/**Gets the minimum and maximum of an array of objects of type T.@param a an array of objects of type T@return a pair with the min and max value, or null if a isnull or empty*/public static <T extends Comparable> Pair<T> minmax(T[] a){if (a == null || a.length == 0) return null;T min = a[0];T max = a[0];for (int i = 1; i < a.length; i++){if (pareTo(a[i]) > 0) min = a[i];if (pareTo(a[i]) < 0) max = a[i];}return new Pair<>(min, max);}}

Java虚拟机如何处理泛型 — 泛型擦除

泛型擦除 — Type Erase

Java 中的泛型只有在编译阶段存在,在代码运行的时候是没有泛型的,这也被称为泛型擦除虚拟机会对泛型代码进行泛型擦除, 也就是将所有的类型 T 替换成raw type, raw type就是bound list中的第一个类型, 或者是Object类型 如果没有bounds list

无bound list的情况

//执行完Type Erasure的代码public class Pair{private Object first;private Object second;public Pair(Object first, Object second) {this.first = first;this.second = second;}public Object getFirst() {return first; }public Object getSecond() {return second; }public void setFirst(Object newValue) {first = newValue; }public void setSecond(Object newValue) {second = newValue; }}

有bound list的情况

public class Interval<T extends Comparable & Serializable> implements Serializable{private T lower;private T upper;. . .public Interval(T first, T second){if (pareTo(second) <= 0) {lower = first; upper = second; }else {lower = second; upper = first; }}}

public class Interval implements Serializable{private Comparable lower;private Comparable upper;. . .public Interval(Comparable first, Comparable second) {. . . }}

类型转换

Pair<Employee> buddies = . . .;Employee buddy = buddies.getFirst();

经过泛型擦除之后buddies.getFirst返回的是Object类型, 编译器自动进行类型转换, 转为Employee类型

桥接方法 — bridge Method

public class Pair<T> {private T first;private T second;public Pair() {first = null; second = null; }public Pair(T first, T second) {this.first = first; this.second = second; }public T getFirst() {return first; }public T getSecond() {return second; }public void setFirst(T newValue) {first = newValue; }public void setSecond(T newValue) {second = newValue; }}

class DateInterval extends Pair<LocalDate>{public void setSecond(LocalDate second){if (pareTo(getFirst()) >= 0)super.setSecond(second);}. . .}

泛型擦除之后

class DateInterval extends Pair // after erasure{public void setSecond(LocalDate second) {. . . }. . .}

这样DataInterval会有两个setSecond方法

public void setSecond(Object second)从pair继承public void setSecond(LocalDate second)

这是两个完全不同的方法, 因为参数不一样. 但是实际上应该是一个方法, DataInterval应该Override从pair继承的setSecond方法.

DateInterval interval = new DateInterval(. . .);Pair<LocalDate> pair = interval; // OK--assignment to superclass//pair的声明类型为Pair所以会调用public void setSecond(Object second)//而无法调用 public void setSecond(LocalDate second)pair.setSecond(aDate);

为了解决这个问题, 编译器会在DataInterval类自动生成一个bridge method

public void setSecond(Object second) {// 调用 public void setSecond(LocalDate second)setSecond((LocalDate) second); }

当下面代码被调用时, pair.setSecond会调用bridge method, 然后bridge method调用实际的setSecond方法

DateInterval interval = new DateInterval(. . .);Pair<LocalDate> pair = interval; // OK--assignment to superclasspair.setSecond(aDate);

In summary, you need to remember these facts about translation of Java generics:

There are no generics in the virtual machine, only ordinary classes and

methods.All type parameters are replaced by their bounds.Bridge methods are synthesized to preserve polymorphism.Casts are inserted as necessary to preserve type safety

Restrictions and Limitations

Type Paramter Cannot be Instantiated with Primitive Types

there is noPair<double>, onlyPair<Double>.因为泛型擦除之后, Pair的类型是Object, double不是Object的子类

在运行时进行的类型查询只适用于原始类型(擦除之后的类型)

对类型的查询只能使用Raw Type (擦除之后的类型)

if (a instanceof Pair<String>) // Error

if (a instanceof Pair<T>) // Error

//getClass method 返回的也是raw typePair<String> stringPair = . . .;Pair<Employee> employeePair = . . .;if (stringPair.getClass() == employeePair.getClass()) // they are equal

Java 不支持泛型数组

在Java中, 数组会记住元素的类型, 如果试图储存其他类型的元素, 就会抛出一个ArrayStoreException

但是对于泛型类型, 擦除会使这种机制无效. 所以Java 不支持泛型数组

Pair<String>[] table = new Pair<String>[10];Object[] objarray = table;//数组中应该存入Pari<String>类型, 但是下面这行代码可以通过array store exception check因为擦除之后的类型是Object//最终会在运行时报错, objarray[0] = new Pair<Employee>();

可以声明通配类型的数组, 然后进行类型转换, 但是结果将是不安全的Pair<String>[] table = (Pair<String>[]) new Pair<?>[10];

If you store a Pair in table[0] and then call a String

method on table[0].getFirst(), you get a ClassCastException.

不能实例化类型变量

You cannot use type variables in an expression such as new T(…).

For example, the following Pair constructor is illegal:

public Pair() {first = new T(); second = new T(); } // Error

Type Variable 不能用static关键字修饰

public class Singleton<T>{private static T singleInstance; // Errorpublic static T getSingleInstance() // Error{if (singleInstance == null) construct new instance of Treturn singleInstance;}}

如果允许声明泛型类的static方法或者属性, 则一个程序可以声明两个类比如Singleton<Random>,Singleton<JFileChooser>, 但是泛型擦除之后, 只有一个Singleton类和singleInstance属性. 则private static Random singleInstanceprivate static JFileChooser singleInstance冲突, 所以不行

泛型类不能继承Throwable接口

public class Problem<T> extends Exception {/* . . . */ } // Error--can't extend Throwable

不能在catch中使用泛型

public static <T extends Throwable> void doWork(Class<T> t){try{do work}catch (T e) // Error--can't catch type variable{Logger.global.info(...)}}

但是可以限定type variable继承Throwable接口

public static <T extends Throwable> void doWork(T t) throws T // OK{try{do work}catch (Throwable realCause){t.initCause(realCause);throw t;}}

注意擦除后的冲突

比如将如下代码加入Pair class

public class Pair<T>{public boolean equals(T value) {return first.equals(value) && second.equals(value); }. . .}

则会出现两个equals method, 解决方法是重新命名冲突的方法

boolean equals(String) // defined in Pair<T>

boolean equals(Object) // inherited from Object

泛型中的继承

如果Manager是Employee的子类, 那么Pair<Manager>Pair<Employee>的子类吗? 答案是"NO"所以以下代码是错的, 不能把Pair<Manager>赋给Pair<Employee>

Manager[] topHonchos = . . .;Pair<Employee> result = ArrayAlg.minmax(topHonchos);

通配符类型 — Wildcard Types

类型通配符的分类

类型通配符:<?> List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型

这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中类型通配符上限:<? extends 类型>

List<? extends Number>:它表示的类型是Number或者其子类型(也就是最大的就是Number类型)类型通配符下限:<? super 类型>

List<? super Number>:它表示的类型是Number或者其父类型(也就是最小的就是Number类型)

public class GenericDemo {public static void main(String[] args) {//类型通配符:<?>List<?> list1 = new ArrayList<Object>();List<?> list2 = new ArrayList<Number>();List<?> list3 = new ArrayList<Integer>();System.out.println("--------");//类型通配符上限:<? extends 类型>//上线Number就是做大的,所以Object不行// List<? extends Number> list4 = new ArrayList<Object>();List<? extends Number> list5 = new ArrayList<Number>();List<? extends Number> list6 = new ArrayList<Integer>();System.out.println("--------");//类型通配符下限:<? super 类型>List<? super Number> list7 = new ArrayList<Object>();List<? super Number> list8 = new ArrayList<Number>();//下线就是Number最小的,所以其子类,Interger不行// List<? super Number> list9 = new ArrayList<Integer>();}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。