传泛型参数java java泛型接口继承泛型类_类型参数


文章目录

泛型类

泛型类的继承

泛型与内部类

泛型接口

泛型接口的实现

泛型方法

形参的类型参数通过实参确定

泛型方法被多个形参推断

返回值的类型参数通过方法返回值赋值的对象确定

形参和返回值用了同一个类型参数

泛型方法定义中的类型推断

静态方法不可以使用泛型类定义的类型参数

泛型方法返回值赋值给形参

可变参数和泛型方法

泛型类和泛型方法

泛型类中的成员泛型方法和静态泛型方法

匿名内部类和泛型

个人理解总结

泛型按照使用方法分为三种情况:

泛型类

泛型接口

泛型方法

泛型类

泛型允许我们使用类型参数Type parameter,它用一个自定义的标识符(T、V、E)来代表一个类型,用< >括住,放在类名后面。然后在类定义中所有使用该类型的地方都用这个标识符来代替,因为在定义这个类的时候还不知道这个具体类型到底是哪个类型。

可使用类型参数Type parameter的地方:声明变量、形参、返回值、强制类型转换。注意不可以用于构造器。

在使用泛型类时(区别于定义泛型类时),< >里面的类型必须被具体类型或定义泛型类时的类型参数所确定。如果是被定义泛型类时的类型参数确定的这种情况,则是把确定类型的任务交给了定义泛型类,这种情况常出现在泛型类的继承、或内部类。

泛型与多态不冲突。当使用泛型类class store时,你既可以使用phone,也可以使用vivoPhone。

泛型类的继承

class two {//定义泛型类}class three extends two {//报错can't resolve symbol 'A',can't resolve symbol 'B'}class two {}class three extends two {}

在第一段代码中,类three的声明会报错,而第二段不会。这是因为,class three属于声明泛型类,而extends two属于使用泛型类。使用泛型类的时候类型参数必须被确定,这里extends two中的A,B被泛型类class three的声明里的A,B所确定。

虽然extends two中的A,B不是具体类型却还是类型参数,但有了声明泛型类class three的保证,A,B最终能被具体类型所确定。

泛型与内部类

此例实现了一个简单的栈,内部原理是链式存储。

// A stack implemented with an internal linked structure.public class LinkedStack {//声明泛型类 private static class Node {//声明泛型类 U item; Node next; Node() { item = null; next = null; } Node(U item, Node next) { this.item = item; this.next = next; } boolean end() { return item == null && next == null; } } private Node top = new Node(); //使用泛型类,但这里被定义泛型类的类型参数确定 public void push(T item) { top = new Node(item, top); }  public T pop() { T result = top.item; if(!top.end()) top = top.next; return result; } public static void main(String[] args) { LinkedStack lss = new LinkedStack(); for(String s : "Phasers on stun!".split(" ")) lss.push(s); String s; while((s = lss.pop()) != null) System.out.println(s); }} /* Output:stun!onPhasers*///:~

类LinkedStack中有个内部类Node,它们都是泛型类。在定义泛型类Node时,类型参数没有被确定,当然也不需要被确定。在使用泛型类private Node top = new Node()时,Node的类型参数被定义泛型类LinkedStack的T所确定,因为这句private Node top = new Node()处于LinkedStack的类定义中,所以这样用也合理。

泛型接口

各方面和泛型类都是类似的。自定义的标识符(T、V、E)来代表一个类型,用< >括住,放在接口名后面。

在使用泛型接口时,< >里面的类型必须被具体类型、定义泛型类或泛型接口时的类型参数所确定。不过一般泛型接口多被具体类型确定。

泛型接口的实现

interface two{}class three implements two{}interface four extends two{ }

上例说明了使用泛型接口时,可以被定义泛型类或泛型接口时的类型参数所确定。

interface two{ A f(A a);}class impl implements two{ public Object f(Object a){ return new Object(); }}

当使用泛型接口时,确定好的具体类型要覆盖掉原来的所有使用类型参数的地方。比如类impl中的f方法中,所有使用A的地方都要用Object替换掉。

泛型方法

自定义的标识符(T、V、E)来代表一个类型,用< >括住,放在方法返回值前面。可以被用到形参声明、方法返回值、方法定义中的变量声明和类型转换。

泛型方法使得该泛型方法的类型参数独立于类而产生变化。泛型方法和泛型类没有关系。

泛型方法的类型参数,一般情况下都是被推断inference出来。更具体地讲,只能被形参或返回值推断出来,当形参和返回值用了同一个类型参数时,二者推断出来的类型必须一样、或者符合多态。

形参的类型参数通过实参确定;返回值的类型参数通过方法返回值赋值的对象确定。这也就是类型参数推断。

当形参的类型参数和返回值的类型参数是同一个时,优先使用形参的推断。因为返回值的类型参数的推断是一种拖延行为。

类的成员方法可以使用定义泛型类的类型参数(注意,这种方法不是泛型方法,只不过使用了类型参数而已);而类的静态方法不可以使用泛型类的类型参数,这是因为只有当创建泛型类对象时类型参数才会被具体类型确定,也就是说,泛型类的类型参数是与对象相关的。那么,很自然地,作为一个static方法肯定不可以使用泛型类的类型参数。static方法想用到泛型只能将其定义为泛型方法。

形参的类型参数通过实参确定

class GenericMethods { public  void f(T x) { System.out.println(x.getClass().getName()); } public void testInt(){ f(1); }}public class testP { public static void main(String[] args) { GenericMethods gm = new GenericMethods(); gm.f(""); gm.f(1); gm.f(1.0); gm.f(1.0F); gm.f('c'); gm.f(gm); gm.testInt(); }}

f方法是一个泛型方法,返回值是void,形参是< >里的T。调用泛型方法,既可以通过对象来调用,例如gm.f("");,此时T被推断为String类型;也可以在除该泛型方法外,且类定义中调用,例如testInt()方法中,此时T被推断为int类型。

泛型方法和自动打包让f方法看起来像是被无限重载了一样。

泛型方法被多个形参推断

public class Test {  public static void main(String[] args) {  /**依靠类型参数推断*/  int i = Test.add(1, 2); //这两个参数都是Integer,所以T为Integer类型  Number f = Test.add(1, 1.2); //这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Number  Object o = Test.add(1, "asd"); //这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Object  /**显式的类型说明*/  int a = Test.add(1, 2); //指定了Integer,所以只能为Integer类型或者其子类  int b = Test.add(1, 2.2); //编译错误,指定了Integer,不能为Float  Number c = Test.add(1, 2.2); //指定为Number,所以可以为Integer和Float  }  //这是一个简单的泛型方法  public static  T add(T x,T y){  return y;  } }

add方法的两个形参都使用到了类型参数T,即使用了同一个类型参数。那么在推断的时候会依靠两个实参的共同最小父类来推断(就好像数学里的最小公倍数)。

返回值的类型参数通过方法返回值赋值的对象确定

class GenericMethods { public  T f2(Object x) { System.out.println(x.getClass().getName()); return (T)x; }}public class testP { public static void main(String[] args) { GenericMethods gm = new GenericMethods(); int o1 = gm.f2(2); String o2 = gm.f2(2);//能通过编译,但运行时报错 gm.f2(1.2);// }}

f2方法是一个泛型方法,返回值是< >里的T,形参是Object x。

执行int o1 = gm.f2(2)后,由于f2方法返回值赋值给了一个int变量,那么这里T就被推断为了int,并且在f2方法的运行过程中,所有使用T的地方都会替换为int,所以return (T)x;实际是执行的return (int)x;。所以,整个运行过程是,2被向上转型为Object,然后强行cast为了int,最后被返回出来。

执行String o2 = gm.f2(2)后,由于这里T就被推断为了String,一个实际为2的Object对象在向上转型为String类型时,会受到RTTI的检查,被发现无法转型后便报错。

执行gm.f2(1.2)后,由于返回值没有赋值给对象,所以T没有被推断出来,也不需要被推断出来。因为没有进行赋值,返回值并不关心。

形参和返回值用了同一个类型参数

class A{}class B extends A{}class GenericMethods { public  T f1(T x) { System.out.println(x.getClass().getName()); return x; }}public class testP { public static void main(String[] args) { GenericMethods gm = new GenericMethods(); int i = gm.f1(1); //String s = gm.f1(1); A a = gm.f1(new B()); //B b = gm.f1(new A()); }}

f1方法是一个泛型方法,返回值是< >里的T,形参也是< >里的T。这种情况只使用形参的类型推断,因为靠返回值推断是一种推延行为。

执行int i = gm.f1(1)时,形参处的T被推断为int,所以没有问题。

执行String s = gm.f1(1)时,形参处的T被推断为int,然后返回值int赋值给一个String类型,所以这里编译报错。

执行A a = gm.f1(new B())时,形参处的T被推断为B。然后返回值B对象赋值给一个A类型,这里向上转型。

执行B b = gm.f1(new A())时,形参处的T被推断为A。然后返回值A对象赋值给一个B类型,这里向下转型,不可以,编译报错。

这里如果是B b = (B) gm.f1(new A())则能够编译通过。这里把工作交给了强制类型转换,编译器便不会报错了,但运行报错。

泛型方法定义中的类型推断

import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class New { public static  Map map() { return new HashMap();//泛型方法定义中使用类型参数 } public static  List list() { return new ArrayList(); } public static void main(String[] args) { Map> sls = New.map(); List ls = New.list(); }}

注意这种情况一般都是发生在return语句里。通过泛型方法的赋值,推断出了泛型方法定义中的类型参数的具体类型。

静态方法不可以使用泛型类定义的类型参数

class two{ public void f(A a){//只是一个使用了类型参数的成员方法 System.out.println(a); }// public static void f1(A a){//静态方法不可以使用泛型类定义的类型参数// System.out.println(a);// } public static  void staticF(A a){//泛型方法,静态方法 System.out.println(a); } public  void memberF(A a){//泛型方法,成员方法 System.out.println(a); }}

注意staticF方法里的A和two里的A没有任何关系。同样,memberF方法里的A和two里的A没有任何关系。因为这二者都是泛型方法。

two里的A是与对象相关的,只有创建对象时才能确定。f1是静态方法,自然不可能与对象相关的东西有关系。

泛型方法返回值赋值给形参

这种情况区别于泛型方法返回值赋值给变量。

import java.util.HashMap;import java.util.Map;class LimitsOfInference { static void f(Map petPeople) { } public static void main(String[] args) { f(New.map()); //报一个警告 }}public class New { public static  Map map() { return new HashMap(); }}

警告内容为:

说明泛型方法中的类型参数并没有被推断出来,所以这里泛型方法返回值只知道是个Map。

而为了这里不报警告,则需要使用显示的类型说明,在调用泛型方法时,在点操作符和方法名之间插入尖括号,然后把具体类型放在里面。这种语法很少见,在这样的非赋值语句中,才需要用到,一般用类型参数推断就可以解决。

import java.util.HashMap;import java.util.Map;class LimitsOfInference { static void f(Map petPeople) { } public static void main(String[] args) { f(New.map()); //不报任何警告 }}public class New { public static  Map map() { return new HashMap(); }}


传泛型参数java java泛型接口继承泛型类_类型参数_02


可变参数和泛型方法

import java.util.*;public class GenericVarargs { public static  List makeList(T... args) { List result = new ArrayList(); for(T item : args) result.add(item); return result; } public static void main(String[] args) { List ls = makeList("A"); System.out.println(ls); ls = makeList("A