泛型(Generics)是一种允许在类、接口和方法中使用类型参数的机制,从而在编译时保证类型安全,并提高代码的复用性。泛型提供了一种解决类型不安全问题的方法,并使代码更具可读性和可维护性。
目录
一.泛型类
-
定义格式:
一个泛型类的定义包括类型参数。类型参数通常用尖括号(
<>
)括起来,并放在类名后面。可以使用一个或多个类型参数。public class 方法名<泛型类型,.....> {}
-
注意事项:
-
1.泛型的默认类型是Object,意味着没有加泛型就可以存储任意类型的数据
-
2.泛型只能编写引用数据类型(包装类在引用数据类型里面),不能用于基本数据类型(int, double, char…)
-
-
泛型类型标识符:
常见的有:
E
、T
、K
、V
-
具体的类型什么时候确认:
二.泛型方法
泛型方法可以分为两种:1.非静态的方法 2.静态的方法
-
1.非静态的方法
public class test<E> { public static void main(String[] args) { test<Integer> ex1 = new test(); //泛型参数 E 被指定为 Integer ex1.printElement(111); // E 被推断为 Integer test<String> ex2 = new test<>(); //泛型参数 E 被指定为 String ex2.printElement("张三"); // E 被推断为 String } // 泛型方法定义 public void printElement(E e) { System.out.println(e); } }
结论:非静态的方法内部的泛型,会根据类的泛型去匹配
-
2.静态的方法
public class GenericsDemo03{ public static void main(String[] args) { String[] arr1 = {"张三","李四","王五"}; Integer[] arr2 = {11,22,33}; Double[] arr3 = {11.1,22.2,33.3}; printArray(arr1); printArray(arr2); printArray(arr3); } //这个类型 T 在调用方法时由传递的数组类型确定 public static<T> void printArray(T[] arr){ System.out.print("["); for (int i = 0; i < arr.length-1; i++) { System.out.print(arr[i] + ","); } System.out.println(arr[arr.length-1] + "]"); } }
结论:静态方法中如果加入了泛型,必须申明出自己独立的泛型。在调用方法,传入实际参数的时候,确定到具体的类型
三.泛型接口
泛型接口有两种方式确定自己的具体类型:
1.实现类,实现接口的时候确定到具体的类型
2.实现类实现接口,没有指定具体类型,就让接口的泛型,跟着类的泛型去匹配
1.实现类,实现接口的时候确定到具体的类型
-
具体代码如下:
public class test { public static void main(String[] args) { } } //自定义接口 interface inter<E> { //接口中的抽象方法,方法的形参类型没想好,可以加一个泛型,然后在接口上也加泛型 void show(E e); } //1:实现类,实现接口的时候确定到具体的类型 class InterAImpl implements inter<String>{ @Override public void show(String s) { } }
2.实现类实现接口,没有指定具体类型,就让接口的泛型,跟着类的泛型去匹配
-
具体代码如下:
public class test { public static void main(String[] args) { } } interface inter<E> { //接口中的抽象方法,方法的形参类型没想好,可以加一个泛型,然后在接口上也加泛型 void show(E e); } //2.实现类实现接口,没有指定具体类型,就让接口的泛型,跟着类的泛型去匹配 class InterBImpl<E> implements inter<E>{ //实现类实现接口的时候,还没想好类型,还可以继续申明泛型 @Override public void show(E e) { } }
四.通配符(Wildcard)
五.泛型的限制
-
不能创建泛型数组:由于泛型类型在运行时被擦除,不能直接创建泛型数组。例如,
new T[10]
是不允许的。 -
不能使用基本数据类型:泛型不能用于基本数据类型,如
int
、char
等。需要使用对应的包装类,如Integer
、Character
。 -
不能实例化泛型类型:不能直接实例化泛型类型,例如
new T()
是不允许的。可以通过传递类类型或使用反射来解决这个问题。 -
类型擦除:泛型类型在编译时会被擦除为其边界类型(通常是
Object
),这意味着在运行时泛型类型的信息丢失。 -
静态上下文限制:泛型类型不能用于静态字段或静态方法中,因为这些静态成员不依赖于具体的泛型类型。
六.类型擦除
基本概念:
Java中的泛型是通过类型擦除实现的。类型擦除是指在编译时,泛型类型信息会被擦除,转化为其边界类型或 Object
。这意味着在运行时,泛型信息不可用。这种设计使得泛型与Java的旧代码兼容,但也带来了一些限制,如无法在运行时获取泛型参数的实际类型。
如何工作
1.类型擦除:
-
泛型类型
T
会被擦除为其上限类型。例如,List<String>
和List<Integer>
都会被擦除为List
。 -
如果泛型有一个上限(如
T extends Number
),T
会被擦除为这个上限(即Number
)。
2.泛型方法和类型参数:
3.实例化泛型类型:
4.类型检查:
示例代码:
-
编译前:
public class Box<T> { private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } }
-
编译后:
public class Box { private Object value; //泛型类型参数 T 被擦除为 Object public Box() { } public void setValue(Object value) { //泛型类型参数 T 被擦除为 Object this.value = value; } public Object getValue() { //泛型类型参数 T 被擦除为 Object return value; } }