泛型类
单参数泛型类:
public class Rectangle{ private T width; private T height; public Rectangle(T width, T height) { this.width = width; this.height = height; } public T getWidth() { return width; } public void setWidth(T width) { this.width = width; } public T getHeight() { return height; } public void setHeight(T height) { this.height = height; } public void showDetails(){ System.out.println("the rectangle, width is " + this.width + " height is " + this.height); }}
通过public class Rectangle<T> {}定义泛型类,在实例化该类时,必须指明泛型T的具体类型,
例如:Rectangle<String> rectangle = new Rectangle<String>();,指明泛型T的类型为String。泛型通配符:
我们知道Ingeter是Number的一个子类,同时在特性章节中我们也验证过Generic<Ingeter>与Generic<Number>实际上是相同的一种基本类型。那么问题来了,在使用Generic<Number>作为形参的方法中,能否使用Generic<Ingeter>的实例传入呢?在逻辑上类似于Generic<Number>和Generic<Ingeter>是否可以看成具有父子关系的泛型类型呢?
为了弄清楚这个问题,我们使用Generic<T>这个泛型类继续看下面的例子:
public void showKeyValue1(Genericobj){ Log.d("泛型测试","key value is " + obj.getKey());}
GenericgInteger = new Generic (123);Generic gNumber = new Generic (456);showKeyValue(gNumber);// showKeyValue这个方法编译器会为我们报错:Generic // cannot be applied to Generic // showKeyValue(gInteger);
通过提示信息我们可以看到Generic<Integer>不能被看作为Generic<Number>的子类。由此可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
回到上面的例子,如何解决上面的问题?总不能为了定义一个新的方法来处理Generic<Integer>类型的类,这显然与java中的多台理念相违背。因此我们需要一个在逻辑上可以表示同时是Generic<Integer>和Generic<Number>父类的引用类型。由此类型通配符应运而生。
我们可以将上面的方法改一下:
public void showKeyValue1(Generic obj){ Log.d("泛型测试","key value is " + obj.getKey());}
类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参 。重要说三遍!此处’?’是类型实参,而不是类型形参 ! 此处’?’是类型实参,而不是类型形参 !再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。
可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。
多参数泛型类:
/** * Created with IntelliJ IDEA. * Description: * User: zhubo * Date: 2017-11-29 * Time: 17:24 */public class Container{ private K key; private V value; public Container(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public void setKey(K key) { this.key = key; } public V getValue() { return value; } public void setValue(V value) { this.value = value; }}
通过public class Container<K, V> {}定义泛型类,在实例化该类时,必须指明泛型K、V的具体类型,
例如:Container<String, String> c1 = new Container<String, String>("name", "Messi");,指明泛型K的类型为String,泛型V的类型为String。泛型接口
public interface Calculator{ public T and(T a , T b);}
public class CalculatorInteger implements Calculator{ @Override public Integer and(Integer a, Integer b) { return a + b; }}
通过public interface Calculator<T> {}定义泛型接口,在实现该接口时,必须指明泛型T的具体类型,
例如:public class CalculatorInteger implements Calculator<Integer>{},指明泛型T的类型为Integer,实例化该类时无需制定泛型类型。泛型方法
public class Generic { publicT getObject(Class c){ T t = null; try { t = c.newInstance(); } catch (Exception e){ e.printStackTrace(); } return t; } public static void main(String[] args) { try{ Generic generic = new Generic(); Object obj = generic.getObject(Class.forName("com.qf58.supplier.entity.others.Generic")); }catch (Exception e) { e.printStackTrace(); } }}
泛型方法说明:
在调用泛型方法的时候,在不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一个父类的最小级,直到Object。在指定泛型的时候,该方法中的几种类型必须是该泛型实例类型或者其子类。
在初始化泛型类的时候,不指定泛型的时候,泛型的类型为Object,就比如ArrayList中,如果不指定泛型,那么这个ArrayList中可以放任意类型的对象。
泛型的好处是在编译的时候进行类型安全检查,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。
泛型方法例子:
/** * Created with IntelliJ IDEA. * Description: * User: zhubo * Date: 2017-11-29 * Time: 19:49 */public class Generate{ private T key; public Generate(T key) { this.key = key; } public T getKey() { return key; } public void setKey(T key) { this.key = key; }}
/** * Created with IntelliJ IDEA. * Description: * User: zhubo * Date: 2017-11-29 * Time: 15:27 */public class DoMain { public static void main(String[] args) { Generategenerate = new Generate<>(123); DoMain domain = new DoMain(); Integer integer = domain.showKeyName(generate); System.out.println(integer); domain.showKeyValue2(generate); } //首先在public与返回值之间的 必不可少,这表明这是一个泛型方法,并进行泛型的T的类型声明 //这个T可以出现在这个泛型方法的任意位置 public void showD(T e){ } //泛型的数量也可以为任意多个 public K show(T t,K k){ K kk = null; return kk; } public void showList(List list){ } public T showKeyName(Generate container){ System.out.println("container key :" + container.getKey()); T test = container.getKey(); return test; } public void showKeyValue(Generate obj){ System.out.println("key value is " + obj.getKey()); } public void showKeyValue1(Generate obj){ } public void showKeyValue2(Generate obj){ System.out.println("key value is " + obj.getKey()); }}
泛型方法与可变参数
再看一个泛型方法和可变参数的例子:
publicvoid printMsg(T ... args){ for(T t : args){ System.out.println(t); } } public static void main(String[] args) { DoMain domain = new DoMain(); domain.printMsg("111",222,"aaa","234.5"); }
静态方法与泛型
静态方法有一种情况需要注意一下,那就是在类中的静态方法使用泛型:静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。
/** * Created with IntelliJ IDEA. * Description: * User: zhubo * Date: 2017-11-29 * Time: 21:10 */public class StaticGenerator{ /** * 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法) * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。 * 如:public static void show(T t){..},此时编译器会提示错误信息: * "StaticGenerator cannot be refrenced from static context" */ public static void show(T t){ }}
泛型方法总结
泛型方法能使方法独立于类而产生变化,以下是一个基本的指导原则:
无论何时,如果你能做到,你就该尽量使用泛型方法。也就是说,如果使用泛型方法将整个类泛型化,那么就应该使用泛型方法。另外对于一个static的方法而已,无法访问泛型类型的参数。所以如果static方法要使用泛型能力,就必须使其成为泛型方法。