泛型
为什么要有泛型?
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,
所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。
因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何
保存,如何管理等是确定的,因此此时把元素的类设计成一个参数,这个类型参数
叫做泛型。
什么是泛型
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类
型或者是某个方法的返回值及参数类型。这个类型参数将在使用时( 例如,
继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实
际的类型参数,也称为类型实参)。Collection<E>, List<E>,
ArrayList<E> 这个<E>就是类型参数,即泛型。
泛型类
class Myclass<T> {
//T代表类型参数,指代任何类型(不包括基本类型8种,要使用其包装类),常用单个大写字母表示,
private T t;
}
public class Test1 {
public static void main(String[] args) {
Myclass<String> myclass1=new Myclass<String>();
Myclass<Integer> myclass2=new Myclass<Integer>();
}
}
- 泛型类可以接受多个类型参数
class Mclass<T,E> {
private T t;
private E e;
}
public class Test1 {
public static void main(String[] args) {
Mclass<String,Integer> mclass=new Mclass<String, Integer>();
}
}
- 引入泛型后,一个泛型类的类型在使用时已经确定好,因此无需向下转型
泛型类的使用-通配符(Wildcards)
? 用于在泛型的使用,即为通配符
public class MyArrayList<E> {...}
// 可以传入任意类型的 MyArrayList
public static void printAll(MyArrayList<?> list) {
...
} /
/ 以下调用都是正确的
printAll(new MyArrayList<String>());
printAll(new MyArrayList<Integer>());
printAll(new MyArrayList<Double>());
printAll(new MyArrayList<Number>());
printAll(new MyArrayList<Object>());
- 通配符-上界
<? extends 上界>
// 可以传入类型实参是 Number 子类的任意类型的 MyArrayList
public static void printAll(MyArrayList<? extends Number> list) {
...
}
- 通配符-下界
<? super 下界>
// 可以传入类型实参是 Integer 父类的任意类型的 MyArrayList
public static void printAll(MyArrayList<? super Integer> list) {
...
}
泛型方法
在方法声明时用<T>表示的方法,<T>中的T称为类型参数,而方法中的T被称为参数
化类型,他不是运行时真正的参数
public <T> void fun(T t) {
System.out.println(t);
}
public <T> T test(T t) { //<T>泛型方法的定义,表示该方法为泛型方法。第二个T表示返回类型是T,第三个T表示参数类型是T.
return t;//类型参数也可以做返回值
}
- 当泛型类与泛型方法共存时,泛型方法始终与自己定义的类型为准、定义时让泛型类和泛型方法的类型不同名,来避免混淆
class Myclass2<T> {
public <T> T func(T t) {
return t;
}
}
public class Test1 {
public static void main(String[] args) {
Myclass2<String> myclass2=new Myclass2<>();
System.out.println(myclass2.func(12));
System.out.println(myclass2.func("123"));
}
}
泛型接口
interface ISubject<T> {
void fun(T t);
}
- 子类在实现接口时有两种实现方式
1)此时子类实现接口时就确定类型
class SujectImpl implements ISubject<String> {
@Override
public void fun(String s) {
}
}
2)子类实现接口时仍然保留泛型
class SubjectImpl2<T> implements ISubject<T> {
@Override
public void fun(T t) {
}
}
类型擦除、
泛型是作用在编译期间的一种机制,实际上运行期间是没有这么多类的,那运行期间
是什么类型呢?这里就是类型擦除在做的事情
jdk 1.5引入 泛型只存在于编译阶段,在进入JVM之前,与泛型有关的信息会被完全擦除 泛型类再进行类型擦除时,未指定泛型的上限,泛型相关信息会被擦除位Object类型,如果有上限,擦除为对应类型的上限。
class Myclass<T,E extends Number> {
public T t;
public E e;
}
T>>>Object
E>>>Number
泛型的注意点
- 泛型类型参数不支持基本数据类型
- 无法实例化泛型类型的对象
- 无法使用泛型类型声明静态的属性
- 无法使用 instanceof 判断带类型参数的泛型类型
- 无法创建泛型类数组
- 无法 create、catch、throw 一个泛型类异常(异常不支持泛型)
- 泛型类型不是形参一部分,无法重载
- 泛型代码与JVM ① 虚拟机中没有泛型,只有普通类和方法。 ② 在编译阶段,所有泛型类的类型参数都会被Object或者它们的限定边界来替换。(类型擦除) ③ 在继承泛型类型的时候,桥方法的合成是为了避免类型变量擦除所带来的多态灾难。 无论我们如何定义一个泛型类型,相应的都会有一个原始类型被自动提供。原始类型的名字就是擦除类型参数的泛型类型的名字。