标签:
一、泛型的基本知识
泛型的语义上理解是“适合许多种类型”, 设计出来的最初目的是希望类或者方法拥有最广泛的表达能力。
1.简单泛型
对于泛型来说最常用也是最简单的应用就是创造容器类
1 package com; 2 3 public class Holder<T> { 4 private T a ; 5 public Holder() { 6 } 7 8 public Holder(T a ) { 9 this.a = a; 10 } 11 12 public static void main(String[]args) { 13 Holder<Integer> holder = new Holder<Integer>(1); 14 System.out.println(holder.a); 15 // holder.a = "1"; //这就报错,因为规定了泛型要接受Integer类型 16 Holder<String> holder2 = new Holder<String>("hello"); 17 //holder2.a = 1; //这也报错,因为规定了泛型只能够接收 String 18 Holder holder3 = new Holder(); 19 holder3.a = 1; 20 holder3.a = "1"; //这里就不报错,因为没给holder3声明泛型。这里就相当于里面存放的Object 21 } 22 }
泛型T处,可以代之任何类型,声明时候指定T为哪个类型(比如上面main函数中指定为Integer)那这个容器就只能装哪种类型,这就在两个方面给出了适用泛型的理由: 未指定之前,它足够“广泛”,可以指代任何类型; 指定之后,它会提供编译器校验,更加安全。
2.复杂泛型--元组
1)什么是元组?
来看看这样一种情景:一个复杂页面中,有A,B两个(甚至更多)模块, 其中A模块对应t_a表, B模块对应t_b表, 那么在同时获取两种数据对象返回的时候,常见的做法是两种:使用List<?> 来接收, 另一种方式是创建一个新的对象C, C对象里面有A,B , 用List<C>来接收。
元组,给了我们在处理这种情景的第三种选择,见下例:
1 package com; 2 3 public class ThreeCouple <A,B,C> { 4 public final A a ; 5 public final B b ; 6 public final C c ; 7 8 public ThreeCouple(A a, B b, C c) {//这里写代码的时候犯下一个小错误,不能够构造方法也用<>包起来。。 9 this.a = a; 10 this.b = b; 11 this.c = c; 12 } 13 14 public String toString() { 15 return a.toString() +","+b.toString()+","+c.toString(); 16 } 17 18 public static void main(String[]args) { 19 ThreeCouple<Integer, Boolean,String> th = new ThreeCouple<Integer, Boolean, String>(1,true,"hello world"); 20 System.out.println(th.toString()); 21 } 22 }
结果如下:
可以看到,元组对于同时传递几个对象来说是个不错的选择。
2)元组的继承
1 package com; 2 3 class TwoCouple<A,B> { 4 public final A a; 5 public final B b; 6 7 public TwoCouple(A a ,B b) { 8 this.a = a; 9 this.b = b; 10 } 11 12 public String toString() { 13 return a.toString()+","+b.toString(); 14 } 15 } 16 public class ThreeCouple <A,B,C> extends TwoCouple<A, B>{ 17 public C c ; 18 19 public ThreeCouple(A a, B b, C c) {//这里写代码的时候犯下一个小错误,不能够构造方法也用<>包起来。。 20 super(a,b); 21 this.c = c; 22 } 23 24 public String toString() { 25 return super.toString()+","+c.toString(); 26 } 27 28 public static void main(String[]args) { 29 ThreeCouple<Integer, Boolean,String> th = new ThreeCouple<Integer, Boolean, String>(1,true,"hello world"); 30 System.out.println(th.toString()); 31 } 32 }
结果如下:
注意一点,元组这里使用public final来修饰成员变量, 这么写在传递多个对象的时候的有点事“格式更简洁”, 并且因为由public final修饰, 元组中的元素在提供给外部使用的时候也不必担心元素值被篡改。
3.泛型接口--泛型可以用于接口
泛型接口应用的例子, 最常见的就是“生成器”,生成器 (generator)是一种专门负责创建对象的类,类似于工厂模式。见下例:
1 package com; 2 3 import java.util.Iterator; 4 import java.util.Random; 5 6 interface GeneratorIntf<T> { 7 T next(); 8 } 9 10 //创建一个咖啡类 11 class Coffee { 12 private static long counter = 0; 13 private final long id = counter++; 14 15 public String toString() { 16 return this.getClass().getSimpleName()+" "+id; 17 } 18 } 19 20 //创建拿铁类,它继承咖啡 21 class Latte extends Coffee{ 22 23 } 24 25 //创建摩卡类 ,它继承咖啡 26 class Mocha extends Coffee { 27 28 } 29 30 //创建卡帕奇诺,它继承咖啡 31 class Cappuccino extends Coffee { 32 33 } 34 35 36 //下面创建一个生成器 37 class CoffeeGenerator<Coffee> implements GeneratorIntf<Coffee>,Iterable<Coffee> { 38 private Class[] types = {Latte.class,Mocha.class,Cappuccino.class};//生成器里面,包含具体产品的类名 39 40 private static Random rd = new Random(47); 41 42 public CoffeeGenerator() { 43 44 } 45 46 private int size = 0;//大小默认为0 47 48 public CoffeeGenerator(int sz) { 49 this.size = sz; 50 } 51 52 @Override 53 public Coffee next() { 54 Coffee coffee = null; 55 try { 56 coffee = (Coffee)types[rd.nextInt(types.length)].newInstance(); //这里,通过类型信息来直接newInstance() 创建对象 57 } catch(Exception e) { 58 e.printStackTrace(); 59 } 60 return coffee; 61 } 62 63 class CoffeeIterator<Coffee> implements Iterator<Coffee>{//创建咖啡迭代器类 64 int count = size; 65 66 public boolean hasNext() { 67 return count>0; 68 } 69 70 public void remove() { 71 throw new UnsupportedOperationException("暂不考虑"); 72 } 73 74 @Override 75 public Coffee next() { 76 count--; 77 return (Coffee) CoffeeGenerator.this.next(); 78 } 79 } 80 81 @Override 82 public Iterator<Coffee> iterator() { 83 return new CoffeeIterator(); 84 } 85 } 86 87 public class GeneratorTest { 88 public static void main(String[]args) { 89 CoffeeGenerator gen = new CoffeeGenerator<>(); 90 for(int i=0;i<5;i++) { 91 System.out.println(gen.next()); 92 } 93 94 for(Object c : new CoffeeGenerator(5)) { 95 Coffee c2 = (Coffee)c; 96 System.out.println(c2); 97 } 98 } 99 }
结果如下:
4.泛型方法--泛型应用于方法之上
泛型方法使得方法能够独立于类而产生变化, 基于公共的代码里面的方法,如果能够使用泛型,则推荐使用泛型方法, 下面是泛型方法的例子:
1 package com; 2 3 public class Test { 4 public void f(int i) { 5 System.out.println(i); 6 } 7 8 public <T> void m(T t) { 9 System.out.println(t); 10 } 11 public <T> T n(T t) { 12 return t; 13 } 14 15 public static void main(String[]args) { 16 Test t = new Test(); 17 t.f(1); 18 19 t.m(1); 20 t.m("abc"); 21 } 22 }
结果如下:
注意泛型方法在返回值位置前面要加<T>
5.匿名内部类中使用泛型
1 package com; 2 3 interface Generator<T> { 4 T next(); 5 } 6 7 public class Customer { 8 private static long counter = 1; 9 private final long id = counter++; 10 private Customer() { 11 } 12 13 public String toString() { 14 return "Customer "+id; 15 } 16 17 public static Generator<Customer> generator() { 18 return new Generator<Customer>() { 19 @Override 20 public Customer next() { 21 return new Customer(); 22 } 23 }; 24 } 25 26 public static void main(String[]args) { 27 Generator<Customer> gen = Customer.generator(); 28 Customer c = (Customer) gen.next(); 29 System.out.println(c); 30 } 31 }
结果如下:
二、泛型中的擦出现象
1.什么是擦出现象?见下例:
1 import java.util.ArrayList; 2 3 4 public class EraseTypeEquivalence { 5 public static void main(String[]args) { 6 Class c1 = new ArrayList<String>().getClass(); 7 Class c2 = new ArrayList<Integer>().getClass(); 8 9 System.out.println(c1 == c2); 10 } 11 }
结果如下:
居然是true, 首先联系一点,每种对象Class,都只会在java虚拟机中创建其类型的一个Class类,用于创建其他对象,
而这里虽然泛型用的参数类型不同,但是它本质上面还是ArrayList的Class对象,所以两个是一样的。 所以是true
上面这种情况,就是所谓的泛型擦除现象。
泛型内部,无法获得任何有关泛型参数的信息。也就是虽然对象传递的时候有差别,但是进入到泛型容器中后对容器来说他们的类型信息就没有了, 都被认为是“无类型之分”了。
标签:
原文地址:http://www.cnblogs.com/kaiguoguo/p/4799638.html