1 泛型
1.1 泛型特点
-
泛型使用主要是在定义类或定义方法时,如果类中(方法中)有一些内容的类型不确定,可以声明一个泛型来暂时代替这些类型
-
一般在类或方法使用时,可以确定泛型的类型
-
泛型的作用
-
设计时,通过使用泛型,可以体现我们对程序整体业务结构的把控。
-
编译时,可以对泛型类型提前做检查,避免程序运行时出现的类型转换问题(下转型问题)
例如:有一个容器,如果没有泛型,容易中所有跟元素相关的类型都是Object
如果逻辑上需要容器存入User,语法上可以存储一个Car
基于逻辑在使用容器中的元素时,都会先将其下转型成User在操作
此时当遇见Car类型的元素就会报错
有了泛型后,可以在使用容器时,通过泛型指定容器中只能存储User
如果偷偷存了一个Car,编译时就会报错
-
编码时,可以省略下转型代码
-
-
泛型的使用分为2部分
-
泛型的定义
-
泛型的确定
-
扩展:泛型擦除
-
泛型的作用只在编译时生效
-
一旦编译完成,虚拟机运行时,就没有泛型这个内容了
-
所有用泛型表示的类型都是Object
1.2 泛型定义
-
在定义类的时候, 类中有些内容的类型不确定,可以声明一个泛型来表示
这些不确定的类型一定是相同类型
class A<T>{ T a ; T b ; public T t1(T i){ } public int t2(String s){} }
-
在定义的类时候,如果类的内容中有多种类型不确定,我们可以声明多个泛型
class A<T1,T2>{ T1 a ; T2 b ; public T2 t1(T1 i) }
-
在定义方法时(以static方法偏多),如果方法中有些内容的类型不确定,可以为其声明泛型
也可以声明多个泛型
一般多用于静态方法的参数类型不确定
public <T> T t1(T a , T b){}
1.3 确定泛型
-
在使用泛型类或泛型方法时,来确定泛型代表的具体类型
确定类泛型
class A<T>{
T i ;
public A(T i){
this.i = i ;
}
}
A<String> a ; //定义变量(成员变量,局部变量,参数变量)
//类A中的i属性是String类型的
new A<String>();//创建对象
class B extends A<String>{} //继承父类
class B implements A<String>{} //实现接口
确定方法泛型
//泛型方法
public static <T> T t1(T a){}
//对于方法的使用,就一种情况,调用方法
//泛型方法中的泛型类型,需要在调用方法时确定,怎么确定呢?
//默认就是传递参数的类型
t1("zs") ;// a参数是String类型, T泛型就是String类型
t1(user) ;//a参数是User类型,T泛型就是User类型
确定多个泛型
class A<T1,T2>{
T1 i ;
T2 j ;
}
A<String,User> a ;
同时定义和确定泛型
interface Box<E>{
void add(E e);
}
class ArrayBox<T> implements Box<T>{
//ArrayBox是一个具体的容器,可以存储一组元素
//但定义ArrayBox类的时候,不确定存储的元素是什么类型的?
//可以声明一个泛型来表示
T[] objects ;
public void add (T t){}
}
1.4 泛型通配符
-
在本应确定泛型的时候,却无法确定泛型,可以使用?通配符代替,表示什么类型都行,目的是让编译通过
// 定义泛型 class A<T>{ T i ; } //情况一 class B{ A<Object> a ; //此时传递确定了具体泛型的属性值,就报错 //a = new A<String>(); //如何避免错误 用?代替Object A<?> a ; } B b = new B(); b.a = new A<String>(); b.a = new A<User>(); //情况二 class B{ //方法中有一个A类型的参数,在定义这个参数时,不确定A中的泛型T //调用方法传递参数时,再确定类型 //此时传递的参数a如果确定了具体的泛型,就会报错 //t1(new A<String>()) //使用?代替Object t1(A<?> a) public void t1(A<Object> a){ } } t1(new A<String>()); t1(new A<User>())
注意:不能带泛型强转
class A<T>{
T i ;
}
A<Object> a = new A<Object>();
a.i = "zs" ;
Object o = a ;
....
A<String> b = (A<String>)a ; //错误
A<Object> c = (A<Object>)o ;
Object[] os = new Object[]{"a","b","c"};
String[] ss = (String[])os ; //错误
1.5 泛型的边界
-
基于通配符?使用的情况下
-
?使用的情况是:应该确定泛型了,但依然无法确定
-
边界的特点是:虽然无法确定具体的泛型,但能确定泛型的一个范围(边界)
class A extends Object{} class B extends A{} class C extends D{} class X<T>{ T i ; } class Y{ X<?> x1 ; //x1 = new X<String>() , x1 = new X<User>(); X<? extends A> x2 ;//x的泛型必须是A或A的子类 //x2 = new X<A>(); x2 = new X<B>(); //x2 = new X<String>(); 错误 X<? super B> x3 ; //x的泛型必须是B或B的父类 //x3 = new X<A>() , x3 = new X<B>(),x3 = new X<Object> //x3 = new X<String>(). x3 = new X<C>() 错误 }