Java基础系列12: 泛型
本文是作者的读书笔记和心得整理,部分内容来源于网络,如有侵权,请联系作者。
什么是泛型
直接看例子
List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);
for(int i = 0; i< arrayList.size();i++){
String item = (String)arrayList.get(i);
Log.d("泛型测试","item = " + item);
}
这个代码会报错,因为没有给arraylist加泛型,所以无法转换.
注意泛型是会在编译期使用,不会进入到运行期
泛型类的初始化
一个最简单的例子:
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
//在类中声明的泛型整个类里面都可以用,除了静态部分,因为泛型是实例化时声明的。
//静态区域的代码在编译时就已经确定,只与类相关
class A <E>{
T t;
}
//类里面的方法或类中再次声明同名泛型是允许的,并且该泛型会覆盖掉父类的同名泛型T
class B <T>{
T t;
}
//静态内部类也可以使用泛型,实例化时赋予泛型实际类型
static class C <T> {
T t;
}
public static void main(String[] args) {
//报错,不能使用T泛型,因为泛型T属于实例不属于类
// T t = null;
}
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
一个更加具体的例子:
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 " + generic.getKey());
Log.d("泛型测试","key is " + generic1.getKey());
Log.d("泛型测试","key is " + generic2.getKey());
Log.d("泛型测试","key is " + generic3.getKey());
D/泛型测试: key is 111111
D/泛型测试: key is 4444
D/泛型测试: key is 55.55
D/泛型测试: key is false
需要注意的是,不允许对泛型使用instanceof操作
泛型接口
常常被用于各种类的生产器中
//定义一个泛型接口
public interface Generator<T> {
public T next();
}
有两种使用方法:
当实现泛型接口的类,未传入泛型实参时:
/**
* 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
* 即:class FruitGenerator<T> implements Generator<T>{
* 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
*/
class FruitGenerator<T> implements Generator<T>{
@Override
public T next() {
return null;
}
}
当实现泛型接口的类,传入泛型实参时:
/**
* 传入泛型实参时:
* 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>
* 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。
* 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
* 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。
*/
public class FruitGenerator implements Generator<String> {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
泛型通配符
public void showKeyValue1(Generic<?> obj);
这里就是一个不确定是什么类型的通配符
泛型方法
如何定义一个类型未知的方法,看下面这个例子:
/**
* 泛型方法的基本介绍
* @param tClass 传入的泛型实参
* @return T 返回值为T类型
* 说明:
* 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法,声明T是一个泛型,可以有多个。
* 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
* 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
* 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
*/
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
Object obj = genericMethod(Class.forName("com.test.test"));
泛型方法和可变参数
引用自https://how2playlife.com/2019/09/13/13%E6%B3%9B%E5%9E%8B/ , 感觉这个例子非常的好
public class 泛型和可变参数 {
@Test
public void test () {
printMsg("dasd",1,"dasd",2.0,false);
print("dasdas","dasdas", "aa");
}
//普通可变参数只能适配一种类型
public void print(String ... args) {
for(String t : args){
System.out.println(t);
}
}
//泛型的可变参数可以匹配所有类型的参数。。有点无敌
public <T> void printMsg( T... args){
for(T t : args){
System.out.println(t);
}
}
//打印结果:
//dasd
//1
//dasd
//2.0
//false
}
注意静态方法要使用泛型的时候必须二次声明.
public class StaticGenerator<T> {
....
....
/**
* 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
* 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
* 如:public static void show(T t){..},此时编译器会提示错误信息:
"StaticGenerator cannot be refrenced from static context"
*/
public static <T> void show(T t){
}
}
泛型的上下界
比如只有Number的子类或者是Integer的父类才能传入.
public class 泛型通配符与边界 {
public void showKeyValue(Generic<Number> obj){
System.out.println("key value is " + obj.getKey());
}
@Test
public void main() {
Generic<Integer> gInteger = new Generic<Integer>(123);
Generic<Number> gNumber = new Generic<Number>(456);
showKeyValue(gNumber);
//泛型中的子类也无法作为父类引用传入
// showKeyValue(gInteger);
}
//直接使用?通配符可以接受任何类型作为泛型传入
public void showKeyValueYeah(Generic<?> obj) {
System.out.println(obj);
}
//只能传入number的子类或者number
public void showKeyValue1(Generic<? extends Number> obj){
System.out.println(obj);
}
//只能传入Integer的父类或者Integer
public void showKeyValue2(Generic<? super Integer> obj){
System.out.println(obj);
}
@Test
public void testup () {
//这一行代码编译器会提示错误,因为String类型并不是Number类型的子类
//showKeyValue1(generic1);
Generic<String> generic1 = new Generic<String>("11111");
Generic<Integer> generic2 = new Generic<Integer>(2222);
Generic<Float> generic3 = new Generic<Float>(2.4f);
Generic<Double> generic4 = new Generic<Double>(2.56);
showKeyValue1(generic2);
showKeyValue1(generic3);
showKeyValue1(generic4);
}
@Test
public void testdown () {
Generic<String> generic1 = new Generic<String>("11111");
Generic<Integer> generic2 = new Generic<Integer>(2222);
Generic<Number> generic3 = new Generic<Number>(2);
// showKeyValue2(generic1);本行报错,因为String并不是Integer的父类
showKeyValue2(generic2);
showKeyValue2(generic3);
}
}