分类:泛型类、泛型接口、泛型方法
注意:1.泛型中T不能为简单类型
2.不能对确切的泛型类型使用instanceof操作 -----》if(ex_num instanceof Generic
=============================================================================
泛型接口:
public interface Generator
public T next();
}
实现类的两种情况:
a.实现类中接口未传入实参
/** * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中 * 即:class FruitGeneratorimplements Generator { * 如果不声明泛型,如:class FruitGenerator implements Generator ,编译器会报错:"Unknown class" */
class FruitGeneratorimplements Generator {
@Override public T next() { return null; } }
b.当实现类中接口传入泛型实参
/**
* 传入泛型实参时:
* 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口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 fruits[rand.nextInt(3)]; } }
=============================================================================
通配符:
问题:用于解决T之间存在继承关系的场景
例子:Generic
和Generic
Integer的父类是Number,但是 Generic
在作为参数时不能将
Generic
作为实参传入
解决方案:使用通配符 ?
************注意: 此处’?’是类型实参,而不是类型形参************
************
此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。************
=============================================================================
泛型方法:调用时机:泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型。
泛型特点:在public/private 后加/ ,参数可以是无数多个,表明是泛型方法,否则是普通方法。
publicT genericMethod(Class tClass)throws InstantiationException ,IllegalAccessException{ T instance = tClass.newInstance(); return instance; }
Object obj = genericMethod(Class.forName("com.test.test"));
//这不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
//同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
public void showKeyValue2(Generic> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
//泛型方法与可变参数
publicvoid printMsg( T... args){
for(T t : args){
Log.d("泛型测试","t is " + t);
}
}
=============================================================================
静态方法与泛型
//静态方法可以声明为泛型方法,
public staticvoid show(T t){ }
//静态方法如果不是泛型方法,方法内不能有泛型参数T ---> 所以如果static方法要使用泛型能力,就必须使其成为泛型方法。
例如:public static void show(T t){..} 此时编译器会提示错误信息:"StaticGenerator cannot be refrenced from static context"
=============================================================================
泛型上下边界
例如:类型实参只准传入某种类型的父类或某种类型的子类。为泛型添加上边界,即传入的类型实参必须是指定类型的子类型
public void showKeyValue1(Generic extends Number> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
Generic generic1 = new Generic("11111");
Generic generic2 = new Generic(2222);
//showKeyValue1(generic1); //这一行代码编译器会提示错误,因为String类型并不是Number类型的子类
showKeyValue1(generic2);
//在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的上添加上下边界,即在泛型声明的时候添加
//publicT showKeyName(Generic publiccontainer),编译器会报错:"Unexpected bound" extends Number> T showKeyName(Genericcontainer){ System.out.println("container key :" + container.getKey()); T test = container.getKey(); return test; }
=============================================================================
泛型数组:
在java中是”不能创建一个确切的泛型类型的数组”的。
List[] ls = new ArrayList //编译异常 Not really allowed[10];
List>[] ls = new ArrayList>[10]; //使用通配符创建泛型数组是可以的
List[] ls = new ArrayList[10]; //这样也可以
List[] lsa = new List [10]; // Not really allowed. Object o = lsa; Object[] oa = (Object[]) o; List li = new ArrayList (); li.add(new Integer(3)); oa[1] = li; // Unsound, but passes run time store check String s = lsa[1].get(0); // Run-time error: ClassCastException.
这种情况下,由于JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,所以可以给oa[1]赋上一个ArrayList而不会出现异常, 但是在取出数据的时候却要做一次类型转换,所以就会出现ClassCastException,如果可以进行泛型数组的声明, 上面说的这种情况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。 而对泛型数组的声明进行限制,对于这样的情况,可以在编译期提示代码有类型安全问题,比没有任何提示要强很多。
下面采用通配符的方式是被允许的:数组的类型不可以是类型变量,除非是采用通配符的方式,因为对于通配符的方式,最后取出数据是要做显式的类型转换的。
List>[] lsa = new List>[10]; // OK, array of unbounded wildcard type. Object o = lsa; Object[] oa = (Object[]) o; Listli = new ArrayList (); li.add(new Integer(3)); oa[1] = li; // Correct. Integer i = (Integer) lsa[1].get(0); // OK