java泛型⽅法⽆参_java泛型详解-绝对是对泛型⽅法讲解最详
细的,没有之⼀
1. 概述
泛型在java中有很重要的地位,在⾯向对象编程及各种设计模式中有⾮常⼴泛的应⽤。
什么是泛型?为什么要使⽤泛型?
泛型,即“参数化类型”。⼀提到参数,最熟悉的就是定义⽅法时有形参,然后调⽤此⽅法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于⽅法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使⽤/调⽤时传⼊具体的类型(类型实参)。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使⽤过程中,操作的数据类型被指定为⼀个参数,这种参数类型可以⽤在类、接⼝和⽅法中,分别被称为泛型类、泛型接⼝、泛型⽅法。
2. ⼀个栗⼦
⼀个被举了⽆数次的例⼦:
List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);
for(int i = 0; i< arrayList.size();i++){
String item = ((i);
Log.d("泛型测试","item = " + item);
}
毫⽆疑问,程序的运⾏结果会以崩溃结束:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
ArrayList可以存放任意类型,例⼦中添加了⼀个String类型,添加了⼀个Integer类型,再使⽤时都以St
ring的⽅式使⽤,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运⽽⽣。
nextint()方法我们将第⼀⾏声明初始化list的代码更改⼀下,编译器会在编译阶段就能够帮我们发现类似这样的问题。
List arrayList = new ArrayList();
...
//arrayList.add(100); 在编译阶段,编译器就会报错
3. 特性
泛型只在编译阶段有效。看下⾯的代码:
List stringArrayList = new ArrayList();
List integerArrayList = new ArrayList();
Class classStringArrayList = Class();
Class classIntegerArrayList = Class();
if(classStringArrayList.equals(classIntegerArrayList)){
Log.d("泛型测试","类型相同");
}
输出结果:D/泛型测试: 类型相同。
通过上⾯的例⼦可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进⼊和离开⽅法的边界处添加类型检查和类型转换的⽅法。也就是说,泛型信息不会进⼊到运⾏时阶段。
对此总结成⼀句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
4. 泛型的使⽤
泛型有三种使⽤⽅式,分别为:泛型类、泛型接⼝、泛型⽅法
4.3 泛型类
泛型类型⽤于类的定义中,被称为泛型类。通过泛型可以完成对⼀组类的操作对外开放相同的接⼝。最典型的就是各种容器类,如:List、Set、Map。
泛型类的最基本写法(这么看可能会有点晕,会在下⾯的例⼦中详解):
class 类名称 {
private 泛型标识 /*(成员变量类型)*/ var;
.....
}
}
⼀个最普通的泛型类:
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常⽤于表⽰泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Generic(T key) { //泛型构造⽅法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型⽅法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
//泛型的类型参数只能是类类型(包括⾃定义类),不能是简单类型
//传⼊的实参类型需与泛型的类型参数类型相同,即为Integer.
Generic genericInteger = new Generic(123456);
//传⼊的实参类型需与泛型的类型参数类型相同,即为String.
Generic genericString = new Generic("key_vlaue");
Log.d("泛型测试","key is " + Key());
Log.d("泛型测试","key is " + Key());
12-27 09:20:04.432 13063-13063/? D/泛型测试: key is 123456
12-27 09:20:04.432 13063-13063/? D/泛型测试: key is key_vlaue
定义的泛型类,就⼀定要传⼊泛型类型实参么?并不是这样,在使⽤泛型的时候如果传⼊泛型实参,则会根据传⼊的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作⽤。如果不传⼊泛型类型实参的话,在泛型类中使⽤泛型的⽅法或成员变量定义的类型可以为任何的类型。
看⼀个例⼦:
Generic generic = new Generic("111111");
Generic generic1 = new Generic(4444);
Generic generic2 = new Generic(55.55);
Generic generic3 = new Generic(false);
Log.d("泛型测试","key is " + Key());
Log.d("泛型测试","key is " + Key());
Log.d("泛型测试","key is " + Key());
Log.d("泛型测试","key is " + Key());
D/泛型测试: key is 111111
D/泛型测试: key is 4444
D/泛型测试: key is 55.55
D/泛型测试: key is false
注意:
泛型的类型参数只能是类类型,不能是简单类型。
不能对确切的泛型类型使⽤instanceof操作。如下⾯的操作是⾮法的,编译时会出错。
if(ex_num instanceof Generic){
}
1
2
4.4 泛型接⼝
泛型接⼝与泛型类的定义及使⽤基本相同。泛型接⼝常被⽤在各种类的⽣产器中,可以看⼀个例⼦:
//定义⼀个泛型接⼝
public interface Generator {
public T next();
}
当实现泛型接⼝的类,未传⼊泛型实参时:
/**
* 未传⼊泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也⼀起加到类中
* 即:class FruitGenerator implements Generator{
* 如果不声明泛型,如:class FruitGenerator implements Generator,编译器会报错:"Unknown class"
*/
class FruitGenerator implements Generator{
@Override
public T next() {
return null;
}
}
当实现泛型接⼝的类,传⼊泛型实参时:
/**
* 传⼊泛型实参时:
* 定义⼀个⽣产器实现这个接⼝,虽然我们只创建了⼀个泛型接⼝Generator
* 但是我们可以为T传⼊⽆数个实参,形成⽆数种类型的Generator接⼝。
* 在实现类实现泛型接⼝时,如已将泛型类型传⼊实参类型,则所有使⽤泛型的地⽅都要替换成传⼊的实参类型
* 即:Generator,public T next();中的的T都要替换成传⼊的String类型。
*/
public class FruitGenerator implements Generator {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
public String next() {
Random rand = new Random();
return Int(3)];
}
}
4.5 泛型通配符
我们知道Ingeter是Number的⼀个⼦类,同时在特性章节中我们也验证过Generic与Generic实际上是相同的⼀种基本类型。那么问题来了,在使⽤Generic作为形参的⽅法中,能否使⽤Generic的实例传⼊呢?在逻辑上类似于Generic和Generic是否可以看成具有⽗⼦关系的泛型类型呢?
为了弄清楚这个问题,我们使⽤Generic这个泛型类继续看下⾯的例⼦:
public void showKeyValue1(Generic obj){
Log.d("泛型测试","key value is " + Key());
}
Generic gInteger = new Generic(123);
Generic gNumber = new Generic(456);
showKeyValue(gNumber);
// showKeyValue这个⽅法编译器会为我们报错:Generic
// cannot be applied to Generic
// showKeyValue(gInteger);
通过提⽰信息我们可以看到Generic不能被看作为`Generic的⼦类。由此可以看出:同⼀种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
回到上⾯的例⼦,如何解决上⾯的问题?总不能为了定义⼀个新的⽅法来处理Generic类型的类,这显然与java中的多台理念相违背。因此我们需要⼀个在逻辑上可以表⽰同时是Generic和Generic⽗类的引⽤类型。由此类型通配符应运⽽⽣。
我们可以将上⾯的⽅法改⼀下:
public void showKeyValue1(Generic> obj){
Log.d("泛型测试","key value is " + Key());
}
类型通配符⼀般是使⽤?代替具体的类型实参,注意了,此处’?’是类型实参,⽽不是类型形参 。重要说三遍!此处’?’是类型实参,⽽不是类型形参 ! 此处’?’是类型实参,⽽不是类型形参 !再直⽩点的意思就是,此处的?和Number、String、Integer⼀样都是⼀种实际的类型,可以把?看成所有类型的⽗类。是⼀种真实的类型。
可以解决当具体类型不确定的时候,这个通配符就是 ?  ;当操作类型时,不需要使⽤类型的具体功能时,只使⽤Object类中的功能。那么可以⽤ ? 通配符来表未知类型。
4.6 泛型⽅法
在java中,泛型类的定义⾮常简单,但是泛型⽅法就⽐较复杂了。
尤其是我们见到的⼤多数泛型类中的成员⽅法也都使⽤了泛型,有的甚⾄泛型类中也包含着泛型⽅法,这样在初学者中⾮常容易将泛型⽅法理解错了。
泛型类,是在实例化类的时候指明泛型的具体类型;泛型⽅法,是在调⽤⽅法的时候指明泛型的具体类型 。
/**
* 泛型⽅法的基本介绍
*@param tClass 传⼊的泛型实参
*@return T 返回值为T类型
* 说明:
* 1)public 与 返回值中间⾮常重要,可以理解为声明此⽅法为泛型⽅法。
* 2)只有声明了的⽅法才是泛型⽅法,泛型类中的使⽤了泛型的成员⽅法并不是泛型⽅法。
* 3)表明该⽅法将使⽤泛型类型T,此时才可以在⽅法中使⽤泛型类型T。
* 4)与泛型类的定义⼀样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常⽤于表⽰泛型。
*/
public T genericMethod(Class tClass)throws InstantiationException ,
IllegalAccessException{
T instance = wInstance();

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。