目录 泛型 泛型类 泛型类的使用 自定义泛型类 泛型接口 泛型方法 类型变量的限定(泛型边界) 泛型通配符 ? ? extends E ? super E 泛型的约束和局限性 泛型类型继承规则 获取泛型的参数类型 泛型代码和虚拟机 总结 附录 泛型 ❓ 什么是泛型?
泛型,指的是一种 「 参数化类型 」,目的是避免强类型语言带来的类型限制,具体的例子如下:
下面是一段使用 python
代码求解两数和的代码
1 2 def sum (a,b ): return a+b
python 本身是一种弱类型语言,这就代表着实现的功能与具体的参数类型无关,上面方法中的 a,b 可以是整数也可以是浮点数,但是这并不影响这个方法所要实现的功能。所以,弱类型语言的好处之一就体现出来了,并不会对具体的类型进行编程,而是对具体的功能进行编程。
然而 Java 是一种强类型语言,这也就使得必须确定每一个方法的参数类型,返回类型,有可能对于两数相加这个功能,具有很多种函数写法。对于简单的相同的功能,却要编码多遍往往是不可忍受的,所以 Java 就引入泛型机制,期望削弱方法对类型的依赖,使开发人员更多的面向功能而不是面向具体实现。
✨ 类型参数化 的含义
类型明确的工作推迟到创建对象或者调用方法时,才去明确的特殊类型.之后将类型作为一种参数传递给集合,这也是一种代码层面上的复用 泛型中的类型在使用时指定,不需要强制类型转换,保证了程序中的类型安全,类型相关的问题,编译器会进行检查 👍使用泛型的优点
把运行时期的问题提前到了编译期间 避免了强制类型转换 优化了程序设计,明确每种集合元素类型 对于类型参数,程序员可能想要内置(plug in)所有的类,在没有过多的限制以及混乱的错误消息的状态下,做所有的事情。一个泛型程序员的任务就是预测出所用类的未来可能有的所有用途
泛型类 首先会介绍常用的一个泛型类Collection
及其子类,随后会介绍如何自定义一个泛型类
泛型类的使用 一个泛型类(generic class) 就是具有一个或多个类型变量的类
1 2 3 4 ArrayList<type> ArrayList<String> arrayList = new ArrayList <String>(); Collection<Object> c = new Collection <String>();
类型变量 含义 E 元素(Element),多用于 java 集合框架 K 关键字(Key) N 数字(Number) T(U、S) 类型(Type) V 值(Value)
1 2 3 4 5 6 7 8 9 10 11 ArrayList<String > arrayList = new ArrayList <String >(); arrayList.add("hello" ); arrayList.add("world" ); arrayList.add("java" ); Iterator<String> it = arrayList.iterator(); while (it.hasNext()){ String s = it.next(); System.out.println(s); } ArrayList<String > arrayList2 = new ArrayList <>();
自定义泛型类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class GenericClass <T> { private T data; public T getData () { return data; } public void setData (T data) { this .data = data; } public static void main (String[] args) { GenericClass<String> genericClass=new GenericClass <>(); genericClass.setData("Generic Class" ); System.out.println(genericClass.getData()); } }
泛型接口 定义一个泛型接口与定义一个泛型类十分相似,具体的代码如下:
1 2 3 interface Inter <T>{ public abstract void show (T t) ; }
✨ 将泛型定义在接口上,实现类在实现泛型接口的时候具有以下两种
已经知道该泛型的明确类型(不常用 ) 并不知道泛型的明确类型(常用 ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class InterImple implements Inter <String>{ @Override public void show (String str) { System.out.println("from normal imple" +str); } } class InterImple2 <T> implements Inter <T>{ @Override public void show (T t) { System.out.println(t); } } public static void main (String[] args) { Inter<String> inter = new InterImple2 <String>(); inter.show("String" ); Inter<Integer> inter2 = new InterImple2 <Integer>(); inter2.show(123 ); Inter<Boolean> inter3 = new InterImple2 <Boolean>(); inter3.show(true ); }
泛型方法 ✨ 使用泛型方法具有如下特点:
泛型方法可以定义在普通类中,也可以定义在泛型类中 不用因为相同的功能而重载多个类 声明一个泛型方法具有如下格式:
下面是一个具体的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class ObjectTool { public <T> void show (T t) { System.out.println(t); } } public static void main (String[] args) { ObjectTool objectTool = new ObjectTool (); objectTool.<String>show("this is string type" ); objectTool.show("hello world" ); objectTool.<Integer>show(100 ); objectTool.show(true ); }
类型变量的限定(泛型边界) 在泛型类或方法需要对类型变量加以约束,这就需要使用类型变量的限定符,一个类型变量或通配符可以有多个限定, 例如:T extends Comparable & Serializable
等
在 Java 中存在两种方式对类型变量进行限定:
对类的限定:public class TypeLimitForClass<T extends List & Serializable>{}
对方法的限定:public static <T extends Comparable<T>> T getMin(T a, T b) {}
1 2 3 4 5 6 7 8 public static <T extends Comparable <T>> T getMin (T a, T b) { return (a.compareTo(b) < 0 ) ? a : b; }
1 2 3 4 5 6 7 8 9 10 11 12 public class TypeLimitForClass <T extends List & Serializable> { private T data; public T getData () { return data; } public void setData (T data) { this .data = data; } public static <T extends Comparable <T>> T getMinListSize (T a, T b) { return (a.compareTo(b) < 0 ) ? a : b; } }
泛型通配符 主要介绍?
,? extends E
,? super E
这三种泛型通配符
1 2 3 class Animal {}class Dog extends Animal {}class Cat extends Animal {}
?
任意的引用类型都可以
1 2 3 Collection<?> c1 = new ArrayList <Animal>(); Collection<?> c2 = new ArrayList <Dog>(); Collection<?> c3 = new ArrayList <Cat>();
? extends E
向下限定,E 及其子类
1 2 3 Collection<? extends Animal > c4 = new ArrayList <Animal>(); Collection<? extends Animal > c5 = new ArrayList <Dog>(); Collection<? extends Animal > c6 = new ArrayList <Object>();
? super E
向上限定
1 2 Collection<? super Animal> c7 = new ArrayList <Object>(); Collection<? super Animal> c8 = new ArrayList <Dog>();
泛型的约束和局限性 ✨ 使用泛型的特点(约束性和局限性)
不能实例化泛型类 静态变量或方法不能引用泛型类型变量,但是静态泛型方法是可以的 基本类型无法作为泛型类型 无法使用 instanceof 关键字或==判断泛型类的类型 泛型类的原生类型与所传递的泛型无关,无论传递什么类型,原生类是一样的 泛型数组可以声明但无法实例化 泛型类不能继承 Exception 或者 Throwable 不能捕获泛型类型限定的异常但可以将泛型限定的异常抛出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 public class GenericRestrict1 <T> { static class NormalClass { } private T data; public void setData () { } private static <K> K getKey (K k) { return k; } public static void main (String[] args) { NormalClass normalClassA = new NormalClass (); NormalClass normalClassB = new NormalClass (); GenericRestrict1<Integer> genericRestrictInteger = new GenericRestrict1 <>(); GenericRestrict1<String> genericRestrictString = new GenericRestrict1 <>(); System.out.println(normalClassA == normalClassB); System.out.println(genericRestrictInteger == genericRestrictInteger); System.out.println(genericRestrictInteger.getClass() == genericRestrictString.getClass()); System.out.println(genericRestrictInteger.getClass()); System.out.println(genericRestrictString.getClass()); GenericRestrict1<String>[] genericRestrict1s; genericRestrict1s = new GenericRestrict1 [10 ]; genericRestrict1s[0 ]=genericRestrictString; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class GenericRestrict2 { private class MyException extends Exception { } public <T extends Throwable > void getException (T t) throws T { try { } catch (Exception e) { throw t; } } }
泛型类型继承规则 ✨泛型类间的继承特点
泛型参数是继承关系的泛型类之间是没有继承关系的 泛型类可以继承其它泛型类;如: public class ArrayListextends AbstractList 泛型类的继承关系在使用中同样会受到泛型类型的影响 获取泛型的参数类型 ❓ Type
是什么?
Type
即,java.lang.reflect.Type, 是 Java 中所有类型的公共高级接口, 代表了 Java 中的所有类型。
Type 体系中类型的包括:数组类型(GenericArrayType)、参数化类型(ParameterizedType)、类型变量(TypeVariable)、通配符类型(WildcardType)、原始类型(Class)、基本类型(Class), 以上这些类型都实现 Type 接口
泛型类型 常用类 参数化类型 泛型 List、Map 数组类型 并不是平时使用的 String[]、byte[],而是带有泛型的数组 T[] 通配符类型 指的是<?>, <? extends T>等等 原始类型 用户自定义的类、枚举、数组、注解等类型 基本类型 java 的基本类型,即 int,float,double 等
1 2 3 4 5 6 7 8 9 10 public interface ParameterizedType extends Type { Type[] getActualTypeArguments(); Type getRawType () ; Type getOwnerType () ; }
泛型代码和虚拟机 虚拟机没有泛型类型对象,所有对象都属于普通类。Java 泛型是 Java1.5 之后才引入的,为了向下兼容。Java 采用了 C++完全不同的实现思想。Java 中的泛型更多的看起来像是编译期用的,Java 中泛型在运行期是不可见的,会被擦除为它的上级类型。如果是没有限定的泛型参数类型,就会被替换为 Object
无论何时定义一个泛型类型, 都自动提供了一个相应的原始类型 ( raw type )。原始类型的名字就是删去类型参数后的泛型类型名。擦除(erased)类型变量,并替换为限定类型(无限定的变量用 Object)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Pair <T>{ private T first; private T second; public Pair () { first = null ; second = null ; } public PairfT first, T second) { this ,first = first; this .second = second; } public T getFirstO { return first; } public T getSecondO { return second; } public void setFirst (T newValue) { first = newValue; } public void setSecond (T newValue) { second = newValue; } } public class Pair { private Object first; private Object second; public Pair (Object first, Object second) { this ,first = first; this .second = second; } public Object getFirstO { return first; } public Object getSecondO { return second; } public void setFirst (Object newValue) { first = newValue; } public void setSecond (Object newValue) { second = newValue; } }
🎶 C++ 中每个模板的实例化产生不同的类型,这一现象称为模板代码膨账 ,Java 不存在这个问题的困扰
❓ 为什么需要有如此麻烦的泛型擦出机制?
擦除主要的正当理由是从非泛化代码到泛化代码的转变过程,以及在不破坏现有类库的情况下,将泛型融入到 Java 语言
❓ 泛型类型参数将擦除到它的第一个边界(它可能会有多个边界),那么class Interval<T extends Serializable & Comparable>
会切换那个边界?
上述泛型的定义,原始类型用 Serializable 替换 T,而编译器在必要时要向 Comparable 插入强制类型转换。为了提高效率,应该将标签接口(即没有方法的接口)放在边界列表的末尾
总结 ✨ Java 具有如下泛型特点
在使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型.传入不同泛型实参的泛型类在内存上仅存在一个,即还是原来的最基本的类型,在逻辑上我们可以理解成多个不同的泛型类型; Java 泛型只是作用于代码编译阶段 ,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的 class 文件中是不包含任何泛型信息的,泛型信息不会进入到运行时阶段。 🎶Java 泛型转换的事实
虚拟机中没有泛型,只有普通的类和方法 所有的类型参数都用它们的限定类型替换 桥方法被合成来保持多态 为保持类型安全性,必要时插入强制类型转换 附录 Java 泛型详解