一、泛型基础说明
泛型是JDK1.5的一种新特性,它的本质是参数化类型的应用,也就是说操作的数据类型被指定为一个参数,在用到的时候再指定具体的类型。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类,泛型接口和泛型方法。
Java语言中的泛型,它只在程序源码中存在,在编译后的字节码文件中,就已经被替换为原来的原始类型(Raw Type,也称为裸类型)了,并且在相应的地方插入了强制转型代码,因此对于运行期的Java语言来说, ArrayList<Integer>与ArrayList<String>就是同一个类 。所以说泛型技术实际上是Java语言的一颗语法糖,Java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型被称为伪泛型。
类型擦除:泛型附加的类型信息对JVM来说不可见。
举个例子:
public class t7 {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> ll = new ArrayList<>();
List<Integer> kk = new ArrayList<>();
System.out.println(ll.getClass());//输出:class java.util.ArrayList
System.out.println(kk.getClass());//输出:class java.util.ArrayList
System.out.println(ll.getClass() == kk.getClass());//输出:true
就是因为泛型擦除。
}
}
二、泛型分类
1.泛型类
一个泛型类(generic class)就是具有一个或多个类型变量的类。定义一个泛型类十分简单,只需要在类名后面加上<>,再在里面加上类型参数。
举例说明:
//泛型类
public class MyDemo<T>{
private T value;
public MyDemo(T value){
this.value = value;
}
public T getValue(){
return value;
}
public void setValue(T value){
this.value = value;
}
}
//泛型类使用
public static void main(String[] args) throws ClassNotFoundException{
MyDemo<String> demo = new MyDemo("Hello");
String str = demo.getValue();
System.out.println(str);
demo.setValue("hi");
str = demo.getValue();
System.out.println(str);
}
2.泛型方法
表达方式如下:
[权限修饰词] <T1,T2,...,Tn> methods(/*...*/) {/*...*/}
尖括号内表示类型参数列表,即方法变量的类型
public class TestDemo {
@Test
public void should_return_value_when_get_with_params(){
String str = get("Hello","world");
Assert.assertEquals("the str should be equals to Hello","Hello",str);
int num = get(123,"Hello");
Assert.assertEquals("num should be equals to 123","123",num);
}
public static <T, U> T get(T t, U u) {
if (u != null)
return t;
else
return null;
}
}
3.泛型接口
interface Show<T,U>{
void show(T t,U u);
}
class ShowTest implements Show<String,Date>{
@Override
public void show(String str,Date date) {
System.out.println(str);
System.out.println(date);
}
}
三、通配符
通配符表示为:?
(1)T、K、V、E等泛型字母称为有“有类型”,类型具体使用时而定。
(2)?为未知类型,类型参数不确定,可以是任意类型。只能用在声明类型、方法参数上,不能用在定义泛型类上
/**
* ? -->通配符,类型不确定,用于声明 变量|形参 上
* 不能用在:
* 1,创建对象
* 2,创建泛型类 、泛型方法、泛型接口上
*
*/
public class WildcardTest {
public static void main(String[] args) {
//声明
List<?> list = new ArrayList<Integer>();
list = new ArrayList<String>();
list = new ArrayList<Object>();
test(list);
//编译错误,不能创建对象
// list = new ArrayList<?>();
}
public static void test (List<?> list){
}
//?不能用在泛型方法上
/*public static <?> void test2(List<?> list){
}*/
//?不能创建泛型类
/*class Test<?>{
}*/
}
五、泛型上下边界
1.作用
先从通配符’?'说起,已知通配符可以是任意类类型,在实际业务或功中在使用通配符时会遇到很多安全问题如:传入的泛型类没有特定的方法或属性,类型转换错误等等。所以为了防止这些问题的发生,就有了上下边界,用于指定通配符的范围。
2.上边界
<? extends T> 表示该通配符所代表的是T类型的子类
// 上边界指定格式
? extends 指定类型(可以是类也可以是接口)
// 要求
class ? extends 指定类
// 或
class ? implements 指定接口
// 要求继承指定类且必须实现指定接口
? extends 指定类&指定接口
// 如下
? extends A&B
上限<? extends T>不能往里存,只能往外取 (即:只能get)
因为编译器只知道容器里的是Fruit或者Fruit的子类,但不知道它具体是什么类型,所以存的时候,无法判断是否要存入的数据的类型与容器种的类型一致,所以会拒绝set操作。
如果需要经常往外读,则使用<? extends T>
2.下边界
<?super T>表示该通配符所代表的类型是T类型的父类
下限<? super T>往外取只能赋值给Object变量,不影响往里存
因为编译器只知道它是Fruit或者它的父类,这样实际上是放松了类型限制,Fruit的父类一直到Object类型的对象都可以往里存,但是取的时候,就只能当成Object对象使用了。
如果经常往里存就是用<?super T>