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 ;
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>() 错误
}