Java基础笔记(4)-泛型
泛型基础
泛型定义
泛型又称为参数化类型,是在JDK 5之后出现的新特性,解决数据类型的安全性问题
在类声明或实例化的时候只需要指定好需要的具体的类型即可
Java泛型可以保证如果程序在编译的时候没有发出警告,运行时就不会产生ClassCastException异常。同时代码更加简洁、健壮
泛型的作用是:可以在类声明的时候通过一个标识符表示类中某个属性的类型,或者某个方法返回值的类型,或者是参数类型(类似于模板)
1
2
3class Person<T> {
T s; //对应中括号中的T
}
泛型的声明
1 |
|
- 其中,T,K,V等不代表值,而是代表类型
- 任意字母都可以,常用T来表示,是Type的缩写
- Java库使用变量E来表示集合的元素类型;K和V分别表示键和值;T、S、U表示任意类型
注意事项:
T,E只能是引用类型
在指定泛型具体类型之后,可以传入该类型或其子类类型
如果不给定泛型的具体类型,则默认泛型类型为Object
在实际开发的时候可以简写,在new后面的可以简写
1
2ArrayList<Integer> list = new ArrayList<>();
List<Integer> list = new ArrayList<>();
自定义泛型
自定义泛型类:
1 |
|
- 普通成员可以使用泛型(属性、方法)
- 使用泛型的数组,不能初始化(因为在new的时候并不知道应该开多少空间)
- 静态方法中不能使用类的泛型(因为静态的在类加载的时候就创建了,与对象无关)
- 泛型类的类型,是在创建对象时确定的(因为创建对象的时候,需要指定确定类型)
- 如果在创建对象的时候,没有指定类型,默认为Object
自定义泛型接口:
1 |
|
接口中,静态成员也不能使用泛型(与泛型类的规定一样)(接口中的属性都是静态的)
泛型接口的类型,在继承接口或者实现接口的时候确定
没有指定类型,默认为Object
1
2
3
4class C implenments I{};
/ /等价于
class C implentments I<Object, Object>{};
// 但是建议使用下面这种方式,更加规范
自定义泛型方法:
1 |
|
- 泛型方法,可以定义在普通类中,也可以定义在泛型类当中
- 当泛型方法被调用的时候,会确定类型
public void eat(E e){}
,修饰符后面没有尖括号,这是使用了类声明的泛型,而不是泛型方法- 泛型方法可以使用类声明的泛型,也可以使用自己定义的泛型
泛型的继承和通配符
泛型不具备继承性
1
List<Object> list = new ArrayList<String>();//这样书写是错误的
<?>
: 支持任意泛型类型<? extends A>
:支持A类以及A类的子类,规定了泛型的上限(实现接口的限制也是使用extends关键字)<? super A>
:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
泛型擦除
泛型擦除:虚拟机并没有泛型类型对象,所有的对象都属于一个普通的类。无论何时定义一个泛型类型,都会自动提供一个相应的原始类型。类型变量会被擦除,被替换为限定类型(通配符的限制),或者无限定的变量替换为Object
- 虚拟机中没有泛型,只有普通的类和方法
- 所有的类型参数都会替换为它们的限定类型
- 合成桥方法来保持多态
- 为保持类型安全性,必要时会插入强制类型转换
泛型的限制
不能使用基本数据类型实例化类型参数
运行时类型查询只适用于原始类型,不能查询泛型类型
不能创建参数化类型的数组
1
new Pair<String>[10]; // ERROR
不能实例化类型变量
1
2
3
4public Pair(){
first = new T();
second = new T();
}// ERROR不能构造泛型数组
1
T[] mm = new T[2]; // ERROR
泛型类的静态上下文中类型变量无效
- 不能在静态字段或方法中引用类型变量,即不能使用带有类型变量的静态字段和方法
不能抛出或捕获泛型类的实例
- 既不能抛出也不能捕获泛型类的对象
- 泛型类扩展Throwable是不合法的
- catch子句中不能使用类型变量
可以取消对检查型异常的检查
使用泛型类、擦除以及
@SuppressWarnings
注解1
2
3
4@SuppressWarnings("unchecked")
static <T extends Throwable> void throwAs(Throwable t) throws T{
throw (T) t;
}