泛型--2
泛型方法 泛型接口
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
1. 泛型方法
1). 泛型类的局限性
(1). 泛型类局限性的示例
[1]. 实例代码 ----泛型类
class Demo<T>{
public void show(T t){
System.out.println("show:"+ t);
}
public void print(T t){
System.out.println("print:"+ t);
}
}
测试时候的局限性:
[2]. 泛型类的局限性
泛型类定义的泛型,在整个类中都有效。
当泛型类对象明确要操作的具体类型之后,这个泛型类对象对应的所有非静态方法的泛型参数全部固定为泛型类对象已经明确的具体类型。
【解决泛型类的局限性】采用泛型方法
2). 泛型方法的使用
(1). 非静态泛型方法的定义
【规则】通过< >将类型参数定义在方法上。具体是访问修饰符和返回值类型之间
/******************泛型方法*****************/
/****************泛型静态方法*****************/
class DemoXX{
public <T> void show(T t){
System.out.println("show:"+ t);
}
public <T> void print(T t){
System.out.println("print:"+ t);
}
}
public class GenericMethodTest{
public static void main(String[] args){
DemoXXd =new DemoXX();
d.show(new Integer(50));
d.show("ABCDE");
d.print("FGHJI");
}
}
编译通过,打印结果是:
【泛型方法的类型参数】泛型方法定义的类型参数的有效范围是这个方法整个的范围内
(2). 静态泛型方法的定义
[1]. 静态方法可以使用定义在类上的泛型么?
答:不可以!!!!
静态方法不可以访问定义在类上的泛型
【原因】定义在类上的泛型是在使用这个类的对象的时候才能使用的。
换句话说:想让一个方法使用类上定义的泛型,必须在调用这个方法的时候,明确这个方法中类的泛型参数取值到底是什么!!!
只有调用的时候,这个参数取值能够确定,就是正确的。
这个类的泛型对于方法来说相当于未知的,从外界传来的。
那么就是使用这个方法的时候,必须确定下来才可以被使用。
因为这个未知的类型参数一定会影响函数体内部的执行。不明确,函数体就没有办法执行,这样就失去了调用这个函数的意义。
不确定类型参数就一定不能使用,使用了就会编译错误。
【应用到静态方法上】 静态方法和类的对象没有必然的联系。那么类中的泛型就没有办法明确在这个静态方法被调用的时候,如何确定这个未知的参数是什么,就无法执行函数体。所以会编译出错。
[2]. 【规则】通过< >将类型参数定义在静态方法上。具体是静态修饰符和返回值类型之间
class DemoXX{
public static <W> void printStatic(W w){
System.out.println("static method: "+w);
}
}class DemoXX{
public static <W> void printStatic(W w){
System.out.println("static method: "+w);
}
}
测试:DemoXX.printStatic("Benjamin");
打印结果:
(3). 泛型方法和泛型类混合举例
【规则】当泛型类中类型参数的名字和泛型方法中定义的类型参数名字一样的时候,方法参数列表中的类型参数是方法上定义的类型参数。
【类比】 这个和方法内部定义的变量和类中的成员变量重名的时候是一样的。内部的重名变量覆盖外部的重名变量。外部的同名变量只能通过this或者super进行访问。
class DemoMixed<T>{
public void show(T t){
System.out.println("show: "+t);
}
public <T> void print(T t){
System.out.println("print:"+ t);
}
}
public class GenericMixed {
public static void main(String[] args) {
DemoMixed<String>d =new DemoMixed<String>();
d.show("haha"); //编译通过
d.show(4); //编译失败
d.print(4); //编译通过
}
}
分析:public <T>void print(T t)的参数中的T既在类的定义时候出现,也在方法的定义时候出现,就近原则,这个T就是方法中定义的类型参数,覆盖了类上定义的类型参数。
2. 泛型接口
泛型接口的定义和使用
(1). 泛型接口的定义
将类型参数通过< >定义在interface关键字和{ 之间,这样这个接口就被声明成了泛型接口
举例:
interface Inter<T>{
public void show(T t);
}
(2). 泛型接口的使用
[1]. 当接口的实现类可以确定实现的泛型接口上定义的类型参数的时候,就在implements关键字、接口的名字后面的< >中填写上具体的参数类型来替换接口中定义的类型参数。
****【结论】实现这个接口中的参数列表中含有类型参数构成的方法的时候,就直接使用实现类中在接口名称后面<>中声明的具体参数类型来替代接口中原有方法的参数列表中类型参数的部分即可。****必须要这样做!!!!
举例:
/*
*String这个具体的类型替换了接口Inter中定义的类型参数T,
* 这样Inter中含有类型参数T的方法publicvoid show(T t);
* 中的T就一定要使用String来替代才能实现接口中的方法的实现
*
/
class InterImplimplements Inter<String>{
public void show(String t){//String 替代了接口中的T
System.out.println("show: "+ t);
}
}
测试:
InterImpl inter =new InterImpl();//实现类不是泛型类,所以不用明确类型参数
inter.show("ABCDE");//打印出ABCDE
[2]. 当接口实现类不能确定实现的泛型接口上定义的类型参数的时候,做以下两件事情
{1}. 在实现子类名称的后面用<>写上接口中定义的类型参数的标示符
==>说明这个类仍然是泛型类
并且
{2}.在implements关键字、接口的名字后面的< >中也同样写上接口中定义的类型参数的标示符
==>说明这个类的泛型类是从要实现的接口中继承来的
【子类重写的方法】如果含有类型参数的部分,照抄即可。
举例:
/*
*Inter中的show方法的参数照抄实现类、接口中的类型参数 T即可
*/
class InterImplX<T>implements Inter<T>{
public void show(T t){
System.out.println(t+" ...");
}
}
测试:
InterImplX<String> inter =new InterImplX<String>();
//实现类是泛型类,所以一定要明确类型参数
inter.show("DEFGH");//打印出DEFGH
【说明】如果父类也是泛型类,继承这个父类的子类也遵循接口的实现类中的规则。
3. 泛型限定的基本使用
关于泛型限定的来历和具体使用方法在个人总结板块的“代码简化 书写规律---- 重载 多态 泛型 泛型限定 可变参数数组”这里面有详细的说明。这里仅仅简单介绍。
1). 使用通配符? 进行程序扩展 -----泛型通配符的用法I
(1). 使用背景
[1]. 有以下一段程序
public class TestV {
public static void printColl(ArrayList<String>al){
for(String s: al){
System.out.print(s+" ");
}
}
}
测试程序:
ArrayList<String> alS =new ArrayList<String>();
alS.add("ABC");
alS.add("DEF");
alS.add("GHI");
printColl(alS);
/*
需求变化:想在想传入Integer的元素,可以么,如果不可以,怎么修改程序?
*/
不可以在测试程序中传入new ArrayList<Integer>( ); 因为类型参数确定之后,在< >中的具体类型不具有多态性。所以,传入newArrayList<Integer>( );会编译失败。
解决办法:在类重写一个方法
public static void printColl(ArrayList<Integer>al){
for(int i: al){
System.out.print(i+" ");
}
}
但是由于同一个类中,这两个方法参数个数相同,并且由于擦除的原因,两个参数化类型被擦除具体参数之后,都变成相同的原始类型ArrayList了。因此,这两个函数还没有办法写到同一个函数中。
【解决办法】采用<?>进行占位。
public class TestV {
public static void printColl(ArrayList<?> al){
for(int i=0; i<al.size(); i++){
System.out.print(al.get(i)+ " ");
}
}
public static void main(String[] args) {
ArrayList<String>alS =new ArrayList<String>();
alS.add("ABC");
alS.add("DEF");
alS.add("GHI");
printColl(alS);
System.out.println();
System.out.println("*****");
ArrayList<Integer> alI =new ArrayList<Integer>();
alI.add(123);
alI.add(456);
alI.add(789);
printColl(alI);//
}
}
打印结果:
2). 泛型限定-----泛型通配符的用法II
(1). 向下限定
[1]. 格式:<? extends T>:T限定,T的子类扩展
表示?取值的类型只能是T本身或者其子类。
[2]. 举例:
【正确】Vector<? extends Number> x =newVector<Integer>();
由于Integer是Number的子类,所以编译通过。
【错误】Vector<? extends Number> x = new Vector<String>();
由于String不是Number的子类,所以编译不能通过。
(2). 向上限定
[2]. 格式:<? super T>:T限定,T的父类扩展
表示?取值的类型只能是T本身或者其父类。
[2]. 举例:
【正确】Vector<? super Integer> x=new Vector<Number>();
由于Number是Integer的父类,所以编译通过。
【错误】Vector<? super Integer> x = new Vector<Byte>();
由于Byte不是Integer的父类,所以编译不能通过。----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------