泛型概述
1:什么是泛型?:
泛型:“参数化类型”,可以从字面理解,参数化,在我们用参数中,定义方法用形参,调用传递实参。类型
在Java中就是指这个类属于那个类。而参数化类型,顾名思义,就是就是将类型定义为参数(可以称之为类型
形参),然后在调用时传入具体类型(类型实参),在你编写一个泛型类时,就可以用在多处。
2:泛型本质:
本质就是参数化类型,也就是在编写代码是不指定具体参数数据的类型,而在使用中指定一个参数,来确定
编写的代码在处理什么类型的数据。这种参数类型可以用在类,接口和方法中,分别被称为泛型类,泛型接口
泛型方法。
3:好处
泛型程序设计:意味着编写的代码可以被很多不同类型的对象所重用。
好的错误检查,让错误在编译期间就暴露出来。
举例:
List arrayList = new ArrayList();
arraylist.add("tian");
arraylist.add(100);
for(int i = 0; i< arraylist.size(); i++){
String s = (String)arraylist.get(i);
}
上述代码为Java老版本的代码,程序运行时毫无疑问会报错,因为之前的Java list没有对数据类型做限制,它的操作
都是在Object上的,所以一个list结合可以放所有类型的对象,这就造成了很严重的问题,
在Java1.5之后Java团队推出了Java新的能力,泛型,很好的解决了这个问题,之后的代码就变成了这样
List<String> arraylist = new Arraylist<>();
arraylist.add("tian");
arraylist.add(100); //1
在上述代码中,注释1出会直接报错,无法编译,给list集合做了很好的限制,这就是我们需要的泛型。
实例化泛型类型
以List为例,List的源码为泛型接口的定义,
泛型实例化为确定泛型中参数化类型的类型:eg:List<String>
String就实例化了泛型类型。
从上面看出:泛型类有点像普通类的工厂。
泛型通用类
/**
* Box:是一个通用类,
* T:传递给泛型类的类型化参数,他可以采用任何类
* t: 泛型类型T的实例。
*/
public class Box<T> {
private T t;
}
泛型类型命名约定
上例中T就是泛型的类型化参数,一般可以使用大写字母表示,在实际开发中普遍的命名约定为,
E-element:主要用于Java collentions框架使用。
K-key:主要用来表示地图的参数类型,或map等的K.
V-value:主要表示地图参数和map等的值,可K一起用
N-number:主要表示数字。
T-type:表示一个类中的第一个泛型
S,U,V,和T一样不过分别表示第二,第三,第四个泛型。
特性
泛型只会在编译时有效
List<String> strings = new ArrayList<>();
List<Integer> integers = neww ArrayList<>();
if (strings.getClass().equals(integers.getClass())){
System.out.println("泛型测试,类型相同");
}
//这里会输出测试日志,
上面的例子可以测试出,Java泛型在编译后会是我们平常看的类,没有泛型标志,也就是说Java泛型只会在编译
阶段有效,编译过程中,正确检查泛型后,会将泛型的相关信息擦除,也就是说泛型不会进入运行阶段,
也就是说,泛型会在逻辑上让人觉得它有许多不同的类型,但实际上都是相同的基本的编码类。
泛型的三种适用方式:泛型类、泛型接口、泛型方法
泛型类:
泛型类就是上述泛型通用类的代码,
/**
* {class 类名 <泛型类型标识>{ 类主体}}
* Box:是一个通用类,
* T:传递给泛型类的类型化参数,他可以采用任何类
* t: 泛型类型T的实例。
*/
public class Box<T> {
private T t;
}
以T代表泛型类的类型形参,你可以将T看成一个类,这个类是什么类就看你在使用的时候传入的实参了,这个实参
会起到限制作用,只有符合这个实参的操作在会被允许。在不传入实参的情况下这个形参可以是任何类型。
泛型方法
[作用域标识符] <泛型类型标识> [返回类型] 方法名(参数){}
public <T> T getName(T t){
return t;
}
//当调用泛型方法时,在方法名钱的<>中放入具体的类型
String s = class.<String>getName("song");
泛型接口
public interface DataListener<T>{
T next();
}
泛型接口定义给出T泛型参数,在实现接口时会有两种情况
1:不指定泛型参数的具体类型,这时需要实现类是泛型类
public class DataListenerImpl<T> implements DataListener<T>{
@Override
public T next();
}
2:实现泛型接口时直接指定泛型参数的具体类型,这个可以实现普通类
public class DataListenerImpl implements DataListener<String>{
@Override
public String next(){}
}
泛型上下边界
当我们使用泛型时,希望定义的泛型不是被所有的类使用,而是特定的一些类使用时,我们指定泛型的具体类型
这样就违背了泛型的理念,所以泛型还有上下边界的问题,这个上下边界就是限定这个泛型可以被那些类使用。
上下边界的使用
泛型定义时:
<T>:用于定义泛型,类型为未知,类型没有限制.
<T extends classA & interfaceB..>:声明有边界的泛型, 泛型T继承classA,实现接口B。
extends在这里不代表着继承,而是限制的意思,多个参数使用&分割,如果限定中有类,则
类必须放到一个
泛型实例化时(通配符限定)
<?>:标识通配符,用于标识泛型实例化是的类型。
<? extends 父类型>:泛型上边界,用于表示实例化时可以确定的父类型的类型。
<? super 子类型> :泛型下边界,用于表示实例化时可以确定的子类型的类型。
public void keyValue(Apple<? extends Number> obj) {
//确定Apple内的泛型为继承了Number类的类型。
System.out.println(" " + obj.get()) ;
}
泛型擦除
类型擦除就是说Java泛型只能用于在编译期间的静态类型检查,然后编译器生成的代码会擦除相应的类型信息
这样在运行中jvm根本不知道泛型所代表的具体类。因为泛型是1.5版本后引入的,为了保持向下兼容,才有的
类型擦除。
public class Node<T>{
private T data;
}
//编译中通过了类型检查,编译完成后的代码实际是这样的
public class Node{
private Object data;
}
这意味着不管我们声明的Node泛型是什么类型,到运行期间都会变成object,
public class Node<T extends Number>{
private T data;
}
//编译后会是
public class Node{
private Number data;
}
泛型的限制
由于泛型类的擦除,会存在一些适用限制。
泛型类不能使用基本类型。
基本类型会使泛型编译报错。
泛型不允许进行直接实例化
Java泛型不允许进行静态化
静态变了在类中共享,需要确定的类型,因此编译器无法确定要使用的类型,
所以不允许进行静态化。
class StaticSample<T>{
private static T t; //编译前类型检查报错
public static T getT(){ //编译前类型检查报错
return t;
}
}
Java泛型不允许直接进行类型转换(通配符可以)
1:以定下泛型类型的变量不能直接进行类型转换。
List<Integer> integers = new ArrayList<Integer>();
List<Double> doubles = new ArrayList<Double>();
integers = doubles; //这个在编译过程中会报错,类型转换出现异常
2:通配符是可以进行转换的
static void cast(List<?> orgin, List<?> dest){
dest = orgin; //这样是可以进行转换的。
}
Java泛型不允许直接适用instanceof运算符进行运行时类型检查
List<String> strings = new ArrayList<String>();
//这个类型检查是错误的,无法实现。
System.out.println(strings instanceof ArrayList<Double>);
Java泛型不允许创建确切类型的泛型数组
List<Integer>[] list = new ArrayList<Integer>[2];
java泛型不允许作为参数进行重载
因为泛型的擦除,擦除后参数化类就会变的一样,所以无法进行方法重载
public class Test<T>{
public void test(List<Integer> list){}
public void test(LIst<Double> list){}
//这个编译后list会变成一样的,所以不能进行方法重载。
}
网友评论