什么是泛型?
Java 泛型(generics)是 JDK5 引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构。泛型的本质是参数化类型,也就是所操作的数据类型被指定为一个参数。
在 JDK5 之前,定义一个集合可以存储任何的数据类型,意味着它是一个 Object 类型的数据。这样是非常不安全的,必须明确知道存储的每个元素的数据类型,否则容易引发 ClassCastException 异常。如下代码所示:
// 编译正常List list = new ArrayList<>();list.add("java");list.add(100);list.add(true); String str = (String) list.get(0); // 每个元素都需要类型转换Integer integer = (Integer) list.get(1);
复制代码
而有了泛型之后,集合的定义变得有点跟数组一样了。我们知道数组就是必须要求类型一致的,但是数组的长度是固定的,而集合是会动态扩容的。这样我们在设置和获取元素的时候,就不需要考虑类型不一致和转换异常的问题了。
List<String> list = new ArrayList();list.add("java8");list.add("java11");list.add(100); // 编译错误,类型不一致String str = list.get(0); // 不需要进行类型转换
复制代码
泛型类
泛型类没有指定具体的数据类型,此时操作类型是 Object。参数只能是类类型,不能是基本数据类型。
定义语法如下:
class 类名称<泛型标识, 泛型标识, ...> { private 泛型标识 变量名; // ...}
复制代码
使用语法如下:
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();// JDK7后,后面的<>中的具体数据类型可以省略类名<具体的数据类型> 对象名 = new 类名<>();
复制代码
实例:
public class Generic<T> { private T key; public Generic(T key) { this.key = key; } public T getKey() { return key; } public void setKey(T key) { this.key = key; }}
public class MainTest { public static void main(String[] args) { // 如果创建时指定了类型,则会自动转换成对应的类型 Generic<String> generic = new Generic<>("java"); String key = generic.getKey(); // 如果创建时没指定类型,则按照Object类型来操作 Generic genericObj = new Generic("object"); Object obj = genericObj.getKey(); }}
复制代码
从泛型类派生子类
class ChildGeneric<T> extends Generic<T>
复制代码
class ChildGeneric extends Generic<String>
复制代码
泛型接口
实现类如果不是泛型类,接口要明确数据类型。实现类也是泛型类,实现类和接口的泛型类型要一致。
定义语法如下:
interface 接口名称<泛型标识, 泛型标识, ...> { 泛型标识 方法名(); // ...}
复制代码
实例:
public interface Fruit<T> { T getKey();}
// 实现泛型接口的类不是泛型类,必须指明泛型类型。public class Apple implements Fruit<String> { @Override public String getKey() { return "apple"; }}
// 实现泛型接口的类也是泛型类,泛型类型必须一致。public class Apple<T> implement Fruit<T> { private T key; public Apple(T key) { this.key = key; } @Override public T getKey() { return this.key; }}
复制代码
泛型方法
定义语法如下:
修饰符 <T, E, ...> 返回值类型 方法名(参数列表) { 方法体...}
复制代码
public 与返回值中间的<T>非常重要,可以理解为声明此方法为泛型方法。
只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
<T>表明该方法将使用泛型类型 T,此时才可以在方法中使用泛型类型 T。
与泛型类的定义一样,此处 T 可以随便写为任意标识,常见的如 T、E、K、V 等用于表示泛型。
举例:
public class Product<T> { private List<T> list = new ArrayList<>(); public void addProduct(T t) { this.list.add(t); } // 该方法没有使用<T>,只是返回T类型的成员方法,不是泛型方法。 public T getFirst() { return list.isEmpty() ? null : list.get(0); } // 该方法是一个泛型方法,使用<E>声明,返回E类型。 // 该类型由调用方法时指定,泛型方法的类型与类的泛型类型没有关系。 public <E> E getFirst(List<E> list) { return list.isEmpty() ? null : list.get(0); }}
复制代码
类型通配符
类型通配符一般就是使用“?”代替具体的类型实参。所以,类型通配符是类型实参,而不是类型形参。
常用的通配符:
E:Element,一般用来代表集合的元素。
T:Type,一般代表 Java 类。
K:Key,键值。
V:Value,数据值。
N:Number,数值类型。
?:表示不确定的 Java 类型。
类型通配符的上限
<? extends T>,表示这个泛型中的参数必须是 T 或者 T 的子类。
使用固定上边界的通配符的泛型, 就能够接受指定类及其子类类型的数据。
public void getFirst(List<? extends Product> list) { // 这里不能再对list.add操作,因为上限通配符不知道是啥类型 Product product = list.isEmpty() ? null : list.get(0); System.out.println(product);}
复制代码
类型通配符的下限
<? super T>,表示这个泛型中的参数必须是 T 或者 T 的父类。
使用固定下边界的通配符的泛型, 就能够接受指定类及其父类类型的数据。
public void getFirst(List<? super Product> list) { // 这里可以再对list.add操作,因为下限通配符只需要保证是Product的子类即可 Object obj = list.isEmpty() ? null : list.get(0); System.out.println(obj);}
复制代码
类型擦除
泛型是 JDK5 才引进的概念,在这之前是没有泛型的,但是,泛型代码能很好地和之前的版本代码兼容。那是因为泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,我们称之为类型擦除。
无限制类型擦除
// 编译前public class Generic<T> { public T key; public T getKey() { return key; } public void setKey(T key) { this.key = key; }}
// 编译后public class Generic { public Object key; public Object getKey() { return key; } public void setKey(Object key) { this.key = key; }}
复制代码
有限制的类型擦除
// 编译前public class Generic<T extends Number> { public T key; public T getKey() { return key; } public void setKey(T key) { this.key = key; }}
// 编译后public class Generic { public Number key; public Number getKey() { return key; } public void setKey(Number key) { this.key = key; }}
复制代码
擦除方法中类型定义的参数
// 编译前public <T extends Number> T getValue(T value) { return value;}
// 编译后public Number getValue(Number value) { return value;}
复制代码
桥接方法
// 编译前public interface Info<T> { T info(T value);}
public class InfoImpl implements Info<Integer> { @Override public Integer info(Integer value) { return value; }}
// 编译后public interface Info { Object info(Object var);}
public class InfoImpl implements Info { public Integer info(Integer var) { return var; } // 桥接方法,保持接口和类的实现关系 @Override public Object info(Object var) { return info((Integer)var); }}
复制代码
测试结果
public static void main(String[] args) { Method[] methods = InfoImpl.class.getDeclaredMethods(); for (Method method : methods) { System.out.println(method.getName() + " === " + method.getReturnType().getSimpleName()); }}
// 输出结果info === Integerinfo === Object
复制代码
以上就是泛型的全部介绍了,谢谢大家。
评论