Java
泛型深入
- 泛型: 是JDK5之后引入的特性, 可以在编译阶段约束操作的数据类型, 并进行检查
- 泛型的格式:
<数据类型> - 泛型只能支持引用类型, 如若需要基本类型作为约束, 则需要使用对应的包装类
泛型的好处
- 把运行时期的问题提前到了编译期间
- 避免了强制类型转换
详细信息
Java中泛型是伪泛型, 只在编译的时候生效, 在编译后生成的class文件是没有泛型的, 这被称为泛型擦除. 泛型的作用就像是一个看门人, 只在开始的时候检查你的类型, 而在(编译)之后会转换为Object类型, 而取出就又会把类型转换为原来的样子. 这么做是为了兼容JDK5之前的版本.
同时也可以理解为什么泛型只支持引用类型, 因为基本类型无法转换为Object类型
泛型的细节
- 指定泛型的具体类型后, 传递数据时, 可以传入该类类型和其子类类型
- 如果不写泛型, 则默认为
Object类型
泛型类
使用场景: 当一个类中, 某个变量的数据类型不确定时, 就可以定义带有泛型的类
格式: 修饰符 class 类名<类型> { } 范式: public class ArrayList<E> { }
提示
E是不确定的类型, 只有使用ArrayList创建对象的时候才能确定. 此处的E可以理解为一个变量, 但不是用来记录数据的, 而是记录类型的, 可以写成E. T, K, V
E是Element的缩写, 表示元素T是Type的缩写, 表示类型K是Key的缩写, 表示键V是Value的缩写, 表示值
java
import java.util.Arrays;
//类这边的泛型E就相当于定义了一个类型变量, 这样的话其他的方法也可以使用这个变量
public class MyArrayList<E> {
private Object[] elementData = new Object[10];
private int size;
public boolean add(E element) {
elementData[size] = element;
size++;
return true;
}
public E get(int index) {
return (E) elementData[index];
}
@Override
public String toString() {
return Arrays.toString(elementData);
}
}java
public class A03_MyArrayListDemo1 {
public static void main(String[] args) {
MyArrayList<String> list = new MyArrayList<>();
list.add("a");
String s = list.get(0);
System.out.println(s);
System.out.println(list);
}
}泛型方法
- 方法中类型不确定时, 可以使用类型后面定义的泛型, 例如: 上面的
add方法. ==> 所有方法都能用此泛型 - 方法中类型不确定时, 在方法申明上定义自己的泛型 ==> 只有本方法能用
定义方式: 修饰符 <泛型> 返回值类型 方法名(参数类型) { }
范式: public static <E> E get(E e) { }
java
import java.util.ArrayList;
//类名见名知意
public class ListUtil {
//私有构造方法
private ListUtil() {
}
// 方法定义为静态
public static <E> void addAll(ArrayList<E> list, E e1, E e2) {
list.add(e1);
list.add(e2);
}
// ...为扩展运算符, Js也有
public static <E> void addAll(ArrayList<E> list, E... e) {// 这时的e是一个数组
for (int i = 0; i < e.length; i++) {
System.out.println(e[i]);
}
}
}java
ArrayList<String> list1 = new ArrayList<>();
//无论写多少参数都不会报错
ListUtil.addAll(list1, "123", "456", "789", "abc");泛型接口
格式: interface 接口名<泛型> { } 范式: public interface List<E> { }
如何使用一个带有泛型的接口:
- 实现类给出具体类型
- 实现类延续泛型, 创建对象时再确定类型
java
//实现类给出具体类型
public class MyArrayList2 implements List<String> {
@Override
public boolean add(String s) {// 只可以存储String类型的数据
return false;
}
/* 需要重写的方法... */
}
//创建对象时, 就不需要再确定泛型类型了
MyArrayList2 list2 = new MyArrayList2();java
// 实现类延续泛型, 创建对象时再确定类型
public class MyArrayList2<E> implements List<E> {
/* 需要重写的方法... */
@Override
public boolean add(E e) {// 延续泛型, 只能当MyArrayList2被创建对象时, 才可以确定
return false;
}
}
//创建对象时添加泛型
MyArrayList2<String> list2 = new MyArrayList2<>();
list2.add("a");泛型的继承和通配符
- 泛型不具备继承性, 但是数据具有继承性
java
public class Demo1 {
public static void main(String[] args) {
ArrayList<Ye> list1 = new ArrayList<>();
ArrayList<Fu> list2 = new ArrayList<>();
ArrayList<Zi> list3 = new ArrayList<>();
//数据具有继承性
list1.add(new Ye());
list1.add(new Zi());
list1.add(new Fu());
method(list1);
//泛型不具备继承性
method(list2);
method(list3);
}
public static void method(ArrayList<Ye> list) {
}
}
class Ye { }
class Fu extends Ye { }
class Zi extends Fu { }如果无法确定类型, 但可以确定该类型是某个父类的子类, 那么就可以使用通配符?
?单独使用时, 表示不确定的类型
java
public class ForNameDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
//首先将字节码文件加载到内存当中, 获取字节码文件对象
Class<?> clazz1 = Class.forName("pojo.Student");
Class<Student> studentClass = Student.class;
System.out.println(clazz1 == studentClass);
}
}?表示不确定的类型, 它可以进行类型限定
| 通配符使用 | 说明 |
|---|---|
? extends A | 这个不确定的类型是A或A的子类 |
? super A | 这个不确定的类型是A或A的父类 |
java
public class Demo1 {
public static void main(String[] args) {
ArrayList<Ye> list1 = new ArrayList<>();
ArrayList<Fu> list2 = new ArrayList<>();
ArrayList<Zi> list3 = new ArrayList<>();
list1.add(new Ye());
list1.add(new Zi());
list1.add(new Fu());
method(list1);
//不会报错
method(list2);
method(list3);
method1(list2);
method1(list1);
method1(list3);
}
//这个不确定的类型是Ye或Ye的子类
public static void method(ArrayList<? extends Ye> list) {
}
//这个不确定的类型是Fu或Fu的父类, 即Fu, 和Ye, 如果传入Zi就会报错
public static void method1(ArrayList<? super Fu> list) { }
}
class Ye { }
class Fu extends Ye { }
class Zi extends Fu { }使用场景
- 如果类型不确定, 但是能够知道此类型是在某个继承体系下的, 就可以使用泛型的通配符