Java-泛型
今天记录一下Java中的泛型,毕竟大家在项目中经常用到或者看到过。
参考:https://www.jianshu.com/p/95f349258afb
1.什么是泛型
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,Java语言引入泛型的好处是安全简单
2.泛型的特性
在Java SE 1.5之前,没有泛型的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换而这种转换是要求开发者对实际参数类型预知的情况下进行的。对于强制类型转换错误的情况,编译器在编译期间可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,可以提高代码的重用率。
拿我们常用的List集合举个例子:
List mList =new LinkedList();
mList.add("aaa");
int a = (int) mList.get(0);
不用说这样一运行肯定会报一个类型不能转换的错误。注意虽然不正确但编译器没有发现,运行时才会报错,那我们试试加泛型的。
其实我们常用的集合就是加了泛型的,只不过大家可能不在意。
总结:
Object的参数"任意化"在编译期间不能发现错误,运行时可能报错,是个安全隐患,而泛型可以在编译期间就可以检查类型安全。
3.常见的泛型分类
泛型有三种分类:泛型类、泛型接口、泛型方法
3.1泛型类
泛型类的定义方式:
public class generic<泛型通配符>{
private 泛型通配符 genericClass;
//泛型构造方法形参 parameter的类型也为T,T的类型由外部指定
public Generic(T parameter) {
this.genericClass = parameter;
}
}
这个泛型通配符可以随意取值:比如A,B,C,D.(注意得是大写)但我们通常都取?、K、V各自有不同的含义:
- ?:表示不确定的java类型
- T:(type) 表示具体的一个java类型
- K V (key value) 分别代表java键值中的Key Value
- E: (element) 代表Element
我们就以K,V这种的通配符定义一个泛型类,代码如下:
public interface GenericInter<K,V> {
public K getKey();
public V getValue();
}
public class GenericClass<K,V> extends GenericInter{
public K key;
public V value;
public GenericClass(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public Object getKey() {
return key;
}
@Override
public Object getValue() {
return value;
}
}
调用:
GenericClass genericClass =new GenericClass("0","张三");
GenericClass mGenericClass =new GenericClass(1,111);
如果要定义超过两个,三个或三个以上的泛型参数可以使用T1, T2, ..., Tn,列如:
public class GenericClass<T1,T2,T3>{
public T1 t1;
public T2 t2;
public T3 t3;
public GenericClass(T1 t1, T2 t2, T3 t3) {
this.t1 = t1;
this.t2 = t2;
this.t3 = t3;
}
}
3.2泛型接口
我们上面定义泛型类的时候用的就是泛型接口,泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中:
public interface GenericInter<K,V> {
public K getKey();
public V getValue();
}
3.3泛型方法
先说一下泛型类和泛型方法的区别:泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。泛型方法相对比较复杂,我们来看一下:
3.3.1
image.png大家觉得getT()是泛型方法不?答案是:No,
getT()方法虽然使用了泛型,但它的泛型是在泛型类中已经声明了的泛型,所以它才可以使用这个T泛型,getT()不是泛型方法,它算是一个普通成员方法。
image.png
大家觉得showGeneric(genericClass<?> obj)是泛型方法不?答案是:No,
showGeneric(genericClass<?> obj)也是一个普通的方法,只不过使用了泛型通配符?,这也印证了泛型通配符?是一种类型实参
image.png
真正的泛型方法长这样:
class genericClass<T> {
public T t;
public genericClass(T t) {
this.t = t;
}
public T getT(){
return t;
}
//正确的泛型方法
public <T> T getGenericClassT(genericClass<T> obj){
return obj.getT();
}
}
要点:真正的泛型方法,在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T。但T可以出现在这个泛型方法的任意位置, 泛型的数量也可以为任意多个
3.3.2 泛型方法和可变参数
image.png这个没啥好说的。
3.3.3 泛型数组
关于如何正确创建泛型数组:
//错误创建泛型数组的方式
List<String> [] mList =new ArrayList<String>[10];
//正确创建泛型数组方式一,通过通配符?
List<?> [] mListTwo =new ArrayList<?>[10];
//正确创建泛型数组方式二
List<String>[] mListThree = new ArrayList[10];
总结:在java中是不能创建一个确切的泛型类型的数组
4.各种泛型的区别
4.1 List<T>,List<Object>,List<?>的区别?
ArrayList<T> al=new ArrayList<T>(); 指定集合元素只能是T类型
ArrayList<?> al=new ArrayList<?>(); 集合元素可以是任意类型,没有意义,一般只是为了说明用法.
- Object和T不同点在于,Object是一个实打实的类,并没有泛指谁,而T可以泛指Object,比方public void printList(List<T> list){}方法中可以传入List<Object> list类型参数,也可以传入List<String> list类型参数,但是public void printList(List<Object> list){}就只可以传入List<Object> list类型参数,因为Object类型并没有泛指谁,是一个确定的类型
- ?和T区别是?是一个不确定类,?和T都表示不确定的类型 ,但如果是T的话,函数里面可以对T进行操作,比方 T car = getCar(),而不能用? car = getCar()。
4.2 T,Class,Class<T>,Class<?>区别?
- T,是一种具体的类,例如String,List,Map......等等,这些都是属于具体的类。
- Class,是一个类,但Class是存放上面String,List,Map.....。
注:获取Class的三种方式:
1.调用Object类的getClass()方法来得到Class对象
image.png
-
使用Class类的中静态forName()方法获得与字符串对应的Class对象
image.png
3.如果T是Java类型则直接T.calss
-
Classy<T> 在实例化的时候,T要替换成具体类,使用Class<T>还有一个好处就是不用强转.
我们看一下不加泛型,反射创建一个类对象,需要强转:
image.png
使用Class<T>泛型后,不用强转了:
image.png
-
Class<?>它是个通配泛型,?可以代表任何类型,主要用于声明时的限制情况
例如,你可以声明:
public Class<?> clazz;
但不能声明:
public Class<T> clazz;
因为T需要指定类型,所以当不知道定声明什么类型的Class的时候可以定义一个Class<?>,Class<?>可以用于参数类型定义,方法返回值定义等。
以上就是今天的全部内容!