通过几个例子来表现java泛型的特性:泛型出现的最主要目的是能跨多个类工作,如果没有这个目的,不如使用具体类型
1、可重用性:持有对象,泛型的目的之一就是制定容器持有什么类型的对象,而且由编译器保证类型的正确性
public class Holder<T> {
private T a;
public Holder(T a){this.a = a;}
public T getA() {return a;}
public void setA(T a) {this.a = a;}
public static void main(String[] args) {
Holder<Automobile> h = new Holder<Automobile>(new Automobile());
Automobile a = h.getA();
a.f();
}
}
2、元组:持有多个对象
public class TwoTuple<A, B> {
public final A first;
public final B second;
public TwoTuple(A a,B b){
first = a;
second = b;
}
@Override
public String toString() {
return "("+first+", "+second+")";
}
}
public class ThreeTuple<A, B, C> extends TwoTuple<A, B> {
public final C third;
public ThreeTuple(A a, B b,C c) {
super(a, b);
third = c;
}
@Override
public String toString() {
return "("+first+", "+second+", "+third+")";
}
}
public class FourTuple<A, B, C, D> extends ThreeTuple<A, B, C> {
public final D fourth;
public FourTuple(A a, B b, C c,D d) {
super(a, b, c);
fourth = d;
}
@Override
public String toString() {
return "("+first+", "+second+", "+third+", "+fourth+")";
}
}
public class FiveTuple<A, B, C, D, E> extends FourTuple<A, B, C, D> {
public final E fifth;
public FiveTuple(A a, B b, C c, D d,E e) {
super(a, b, c, d);
fifth = e;
}
@Override
public String toString() {
return "("+first+", "+second+", "+third+", "+fourth+", "+fifth+")";
}
}
使用:
public class Amphibian {}
public class Vehicle {}
public class TupleTest {
static TwoTuple<String,Integer> f(){
return new TwoTuple<String,Integer>("hi",47);
}
static ThreeTuple<Amphibian, String, Integer> g(){
return new ThreeTuple<Amphibian, String, Integer>(new Amphibian(), "hi", 47);
}
static FourTuple<Amphibian, String, Integer, Double> h(){
return new FourTuple<Amphibian, String, Integer, Double>(
new Amphibian(), "hi", 47, 11.1);
}
static FiveTuple<Vehicle, Amphibian, String, Integer, Double> k(){
return new FiveTuple<Vehicle, Amphibian, String, Integer, Double>(
new Vehicle(),new Amphibian(), "hi", 47, 11.1);
}
public static void main(String[] args) {
TwoTuple<String, Integer> ttsi = f();
System.out.println(ttsi);
System.out.println(g());
System.out.println(h());
System.out.println(k());
}
}
//(hi, 47)
//(test01.Amphibian@7afa0094, hi, 47)
//(test01.Amphibian@2d8eef25, hi, 47, 11.1)
//(test01.Vehicle@233aa44, test01.Amphibian@def577d, hi, 47, 11.1)
这里没有使用get、set方法,因为final本来就能保证他是安全的
简化:目的在于简化为一个工具类,后面我们可以看到在使用过程中能减少一部分代码并增加可读性:
public class Tuple {
public static <A,B> TwoTuple<A, B> tuple(A a,B b){
return new TwoTuple<A, B>(a, b);
}
public static <A,B,C> ThreeTuple<A, B, C> tuple(A a,B b,C c){
return new ThreeTuple<A, B, C>(a, b, c);
}
public static<A,B,C,D> FourTuple<A,B,C,D> tuple(A a,B b, C c,D d){
return new FourTuple<A, B, C, D>(a, b, c, d);
}
public static <A,B,C,D,E> FiveTuple<A,B,C,D,E> tuple(A a,B b,C c,D d,E e){
return new FiveTuple<A, B, C, D, E>(a, b, c, d, e);
}
}
public class TupleTest2 {
static TwoTuple<String,Integer> f(){
return Tuple.tuple("hi",47);
}
static TwoTuple f2(){return Tuple.tuple("hi",47);}
static ThreeTuple<Amphibian, String, Integer> g(){
return Tuple.tuple(new Amphibian(), "hi", 47);
}
static FourTuple<Amphibian, String, Integer, Double> h(){
return Tuple.tuple(new Amphibian(), "hi", 47, 11.1);
}
static FiveTuple<Vehicle, Amphibian, String, Integer, Double> k(){
return Tuple.tuple(new Vehicle(),new Amphibian(), "hi", 47, 11.1);
}
public static void main(String[] args) {
TwoTuple<String, Integer> ttsi = f();
System.out.println(ttsi);
System.out.println(f2());
System.out.println(g());
System.out.println(h());
System.out.println(k());
}
}
3、通用堆栈类与末端哨兵:
public class LinkedStack<T> {
//内部类,定义节点
private static class Node<U>{
U item;
Node<U> next;
Node(){item = null;next = null;}
Node(U item,Node<U> next){
this.item = item;
this.next = next;
}
//末端哨兵
boolean end(){return item==null&&next==null;}
}
private Node<T> top = new Node<T>();
public void push(T item){
top = new Node<T>(item, top);//第一次push的时候最底层是item=null,next=null
}
public T pop(){
T result = top.item;
//如果还没到低
if(!top.end())top = top.next;
return result;
}
public static void main(String[] args) {
LinkedStack<String> lss = new LinkedStack<String>();
for(String s:"Phaser on stun!".split(" "))lss.push(s);
String s;
//最后的节点top和next都是null,跳出循环
while((s=lss.pop())!=null)System.out.println(s);
}
}
//stun!
//on
//Phaser
4、实现一个返回随机类型的list,当然类型那个由你去指定
//利用randomList处理不同的元素
public class RandomList<T> {
private ArrayList<T> storage = new ArrayList<T>();
private Random rand = new Random(47);
public void add(T item){storage.add(item);}
public T select(){
return storage.get(rand.nextInt(storage.size()));
}
public static void main(String[] args) {
RandomList<String> rs = new RandomList<String>();
for(String s:("The quick brown for jumped over "
+"the lazy brown dog").split(" "))
rs.add(s);
for(int i = 0;i<11;i++) System.out.println(rs.select()+" ");
}
}
5、泛型创建生成器:工厂方法的一种应用
生成器接口和实体类
public interface Generator<T> {
T next();
}
public class Coffee {
private static long counter = 0;
private final long id = counter++;
@Override
public String toString() {
return getClass().getSimpleName()+" "+id;
}
}
public class Latte extends Coffee {}
public class Americano extends Coffee {}
public class Breve extends Coffee {}
public class Cappuccino extends Coffee {}
public class Mocha extends Coffee {}
使用:他创造对象的方式是随机的,如果你需要的不是随机的对象当然需要额外的条件,即使java编程思想里面说“他不需额外的信息”,毕竟不存在不干活就收获的方法
public class CoffeeGenerator implements Generator<Coffee>, Iterable<Coffee> {
private Class[] types = {Latte.class,Mocha.class,Cappuccino.class,
Americano.class,Breve.class};
private static Random rand = new Random(47);
public CoffeeGenerator(){}
private int size = 0;
public CoffeeGenerator(int sz){size = sz;}
@Override
public Iterator<Coffee> iterator() {
return new CoffeeIterator();
}
@Override
public Coffee next() {
try {return (Coffee)types[rand.nextInt(
types.length)].newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
class CoffeeIterator implements Iterator<Coffee>{
int count = size;
@Override
public boolean hasNext() {
return count>0;
}
@Override
public Coffee next() {
count--;
return CoffeeGenerator.this.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
public static void main(String[] args) {
CoffeeGenerator gen = new CoffeeGenerator();
for(int i=0;i<5;i++){
System.out.println(gen.next());
}
//这里默认会使用Iterator
for(Coffee c:new CoffeeGenerator(5)){
System.out.println(c);
}
}
}
//Americano 0
//Latte 1
//Americano 2
//Mocha 3
//Mocha 4
//Breve 5
//Americano 6
//Latte 7
//Cappuccino 8
//Cappuccino 9
如果仅仅是通过定义接口的方法去定义生成器,这往往是不够的,利用类型信息,我们可以更进一步:
//一个通用的Generator
public class BasicGenerator<T> implements Generator<T>{
private Class<T> type;
public BasicGenerator(Class<T> type){this.type = type;}
@Override
public T next() {
try {
return type.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static<T> Generator<T> create(Class<T> type){
return new BasicGenerator<T>(type);
}
}
使用:
public class CountedObject {
private static long counter = 0;
private final long id = counter++;
public long id(){return id;}
public String toString() {return "CountedObject "+id;};
public static void main(String[] args) {
Generator<CountedObject> gen = BasicGenerator.create(CountedObject.class);
for(int i = 0;i<5;i++)System.out.println(gen.next());
}
}
甚至我们可以利用生成器其填充一个集合:
public class Generators {
public static <T> Collection<T> fill(Collection<T> coll,Generator<T> gen,int n){
for(int i = 0;i<n;i++){
coll.add(gen.next());
}
return coll;
}
}
6、生成器的另一个例子:生成fibonacci数列
public class Fibonacci implements Generator<Integer> {
private int count = 0;
public int fib(int n){
if(n<2)return 1;
return fib(n-2)+fib(n-1);
}
@Override
public Integer next() {
return fib(count++);
}
public static void main(String[] args) {
Fibonacci gen = new Fibonacci();
for(int i = 0;i<18;i++){
System.out.print(gen.next()+" ");
//1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
}
}
}
当然也可以通过适配器:
public class IterableFibnoacci extends Fibonacci
implements Iterable<Integer> {
private int n;
public IterableFibnoacci(int count){n = count;}
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public Integer next() {
n--;
return IterableFibnoacci.this.next();
}
@Override
public boolean hasNext() {return n>0;}
};
}
public static void main(String[] args) {
for(int i:new IterableFibnoacci(18)){
System.out.print(i+" ");
}
//1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
}
}
7、简化创建容器对象的工具类:
//一个能节省代码的容器创造类
public class New {
public static <K,V> Map<K,V> map(){
return new HashMap<K,V>();
}
public static <T> List<T> list(){
return new ArrayList<T>();
}
public static <T> LinkedList<T> lList(){
return new LinkedList<T>();
}
public static <T> Set<T> set(){
return new HashSet<T>();
}
public static <T> Queue<T> queue(){
return new LinkedList<T>();
}
public static void main(String[] args) {
Map<String, List<String>> sls = New.map();
//相比这种形式能节省很多代码
//Map<String,List<? extends String>> sample = new HashMap<String, List<? extends String>>();
//如果要调用一个泛型方法f(New.<Person,List<Pet>>map())
List<String> ls = New.list();
LinkedList<String> lls = New.lList();
Set<String> ss = New.set();
Queue<String> qs = New.queue();
}
}
8、Set集合工具:实现对象的交集等操作
public class Sets {
//把两个参数合并在一起
public static <T> Set<T> union(Set<T> a,Set<T> b){
Set<T> result = new HashSet<T>(a);
result.addAll(b);
return result;
}
//返回交集
public static <T> Set<T> intersection(Set<T> a,Set<T> b){
Set<T> result = new HashSet<T>(a);
result.retainAll(b);
return result;
}
//移除元素
public static <T> Set<T> difference(Set<T> superset,Set<T> subset){
Set<T> result = new HashSet<T>(superset);
result.removeAll(subset);
return result;
}
//返回交集之外的所有元素
public static <T> Set<T> complement(Set<T> a,Set<T> b){
return difference(union(a, b), intersection(a, b));
}
}
9、泛型与匿名内部类
public class Customer {
private static long counter = 1;
private final long id = counter++;
private Customer(){}
@Override
public String toString() {
return "Customer "+id;
}
public static Generator<Customer> generator(){
return new Generator<Customer>() {
@Override
public Customer next() {
return new Customer();
}
};
}
}
public class Teller {
private static long counter = 1;
private final long id = counter++;
private Teller(){}
@Override
public String toString() {
return "Teller "+id;
}
public static Generator<Teller> generator =
new Generator<Teller>() {
@Override
public Teller next() {
return new Teller();
}
};
}
方法
public class BankTeller {
public static void serve(Teller t,Customer c){
System.out.println(t+" serve "+c);
}
public static void main(String[] args) {
Random rand = new Random(47);
Queue<Customer> line = new LinkedList<Customer>();
Generators.fill(line, Customer.generator(), 15);
List<Teller> tellers = new ArrayList<Teller>();
Generators.fill(tellers, Teller.generator, 4);
for(Customer c:line){
serve(tellers.get(rand.nextInt(tellers.size())),c);
}
}
}
//Teller 3 serve Customer 1
//Teller 2 serve Customer 2
//Teller 3 serve Customer 3
//Teller 1 serve Customer 4
//Teller 1 serve Customer 5
//Teller 3 serve Customer 6
//Teller 1 serve Customer 7
//Teller 2 serve Customer 8
//Teller 3 serve Customer 9
//Teller 3 serve Customer 10
//Teller 2 serve Customer 11
//Teller 4 serve Customer 12
//Teller 2 serve Customer 13
//Teller 1 serve Customer 14
//Teller 1 serve Customer 15
10、构建复杂模型的例子:比如一个商店里面有多个集合:走廊包括货架,货架包括商品,这就存在一种嵌套关系:
public class Product {
private final int id;
private String description;
private double price;
public Product(int id, String description, double price) {
this.id = id;
this.description = description;
this.price = price;
System.out.println(toString());
}
@Override
public String toString() {
return id+ ": "+description+".price: $"+price;
}
public void priceChange(double change){
price+=change;
}
public static Generator<Product> generator = new Generator<Product>() {
private Random rand = new Random(47);
@Override
public Product next() {
return new Product(rand.nextInt(1000),"Test",
Math.round(rand.nextDouble()*1000.0+0.99));
}
};
}
public class Shelf extends ArrayList<Product> {
public Shelf(int nProducts){
Generators.fill(this, Product.generator, nProducts);
}
}
class Aisle extends ArrayList<Shelf>{
public Aisle(int nShelves,int nProducts){
for(int i = 0;i<nShelves;i++){
add(new Shelf(nProducts));
}
}
}
public class Store extends ArrayList<Aisle> {
public Store(int nAisles,int nShelves,int nProducts){
for(int i = 0;i<nAisles;i++){
add(new Aisle(nShelves, nProducts));
}
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
for(Aisle a:this){
for(Shelf s:a){
for(Product p:s){
result.append(p);
result.append("\n");
}
}
}
return result.toString();
}
public static void main(String[] args) {
//14条走廊,每条走廊5个货架,每个货架10个商品
System.out.println(new Store(14, 5, 10));
}
}
//258: Test.price: $401.0
//861: Test.price: $161.0
//868: Test.price: $418.0
//207: Test.price: $269.0
//551: Test.price: $115.0
//278: Test.price: $805.0
//当然还有更多....
11、泛型创建类型实例:利用newInstance并不是万金油,因为他必须需要一个默认构造器,当然不能是new T[]的方法
(1)、工厂方法:
public interface FactoryI<T> {
T create();
}
public class IntegerFactory implements FactoryI<Integer>{
@Override
public Integer create() {
return new Integer(0);
}
}
public class Widget {
public static class Factory implements FactoryI<Widget>{
@Override
public Widget create() {
return new Widget();
}
}
}
public class Foo2<T> {
private T x;
public <F extends FactoryI<T>> Foo2(F factory){
x = factory.create();
}
public T getX() {
return x;
}
}
public class FactoryConstraint {
public static void main(String[] args) {
Foo2<Integer> i =new Foo2<Integer>(new IntegerFactory());
System.out.println(i.getX());
new Foo2<Widget>(new Widget.Factory());
}
}
(2)、模版方法:
public abstract class GenericWithCreate<T> {
final T element;
public GenericWithCreate() {
// TODO Auto-generated constructor stub
element = create();
}
abstract T create();
}
public class X {
}
public class Creator extends GenericWithCreate<X> {
@Override
X create() {
return new X();
}
void f(){
System.out.println(element.getClass().getSimpleName());
}
}
public class CreatorGeneric {
public static void main(String[] args) {
Creator c = new Creator();
c.f();
}
}
12、擦除动机:使得泛化的客户端可以用非泛化的类库来使用,反之亦然(迁移兼容性)
“泛型是JDK1.5才出现的,所以为了兼容,采用了擦除的方式实现。泛型类型只有在静态类型检查期间才出现,在此之后,程序中所有泛型类型都被擦除,替换为他们的非泛型上界。例如List<T>将被擦除为List,而普通的类型变量在未指定边界的情况下将被擦除为Object。”
擦除可以是现有泛型客户端代码能够在不改变的情况下继续使用,直至客户端准备好用泛型重写这些代码,无论什么情况,我们都要在编写泛型代码是提醒自己:这只是一个Object,擦除方法或类的内部移除了有关实际类型的信息,编译器仍旧可以确保在方法或者类中使用类型内部的一致性
例如List<T>泛型在运行内部会进行擦除,但是运行之后能辨识的原因是RTTI(运行时类型判定)
比如:
public class Fnorkle {}
public class Frob {}
public class Particle<POSITION, MOMENTUM> {}
public class Quark<Q> {}
public class LostInformation {
public static void main(String[] args) {
List<Frob> list = new ArrayList<Frob>();
System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
Map<Frob,Fnorkle> map = new HashMap<Frob, Fnorkle>();
System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
Quark<Fnorkle> quark = new Quark<Fnorkle>();
System.out.println(Arrays.toString(quark.getClass().getTypeParameters()));
Particle<Long, Double> p = new Particle<Long, Double>();
System.out.println(Arrays.toString(p.getClass().getTypeParameters()));
}
}
//[E]
//[K, V]
//[Q]
//[POSITION, MOMENTUM]
又比如:
public class ErasedTypeEquivalence {
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1==c2);
}
}
我们会发现输出的是true
内部把具体类型擦除成Object,用这两个类去证明:
//public class SimpleHoder{
// private Object obj;
// get\set方法
//}
//public class GenericHolder<T>{
// private T obj;
// ...
//}
以这两种方式去去持有一个类,而他们反编译的字节码基本是一样的,对进入set的类型检查是不需要的,因为这由编译器进行,而get返回值的转型仍然是需要的,在前者,他已经规定是Object,所以要转型,但是后者由于RTTI,不需要强转
重载:
public class UseList<W,T>{
void f(List<T> v){}
void f(List<W> v){}
}这两种方法签名是一样的
参考类型擦擦除文章:http://blog.csdn.net/caihaijiang/article/details/6403349#t3
在泛型内部,无法获得任何有关泛型参数类型的信息,而一致性,可以用下面这个例子体现:
public class FilledListMaker<T> {
List<T> create(T t,int n){
List<T> result = new ArrayList<T>();
for(int i = 0;i<n;i++) result.add(t);
return result;
}
public static void main(String[] args) {
FilledListMaker<String> stringMaker = new FilledListMaker<String>();
List<String> list = stringMaker.create("Hello", 4);
System.out.println(list);
}
}
我们在看一个例子,这是在内部的擦除,当然这也是合情合理的:
public class HasF {
public void f(){System.out.println("f()");}
}
public class Manipulator<T> {
T obj;
public Manipulator(T obj) {this.obj = obj;}
public void manipulate(){//我们能看到在泛型代码内部,类型是被擦除的
//obj.f();
}
public T getObj() {
return obj;
}
}
public class Manipulation {
public static void main(String[] args) {
HasF hf = new HasF();
Manipulator<HasF> man = new Manipulator<HasF>(hf);
man.manipulate();//自然不能待用f()
HasF anoHf = man.getObj();//但是这对我们目前为止学过的并没有冲突,因为在外部,编译器能运行时识别这是什么类
anoHf.f();//f()
}
}
所以当我们使用instanceof的时候,很一般的想法:这当然是不可能的,因为擦除
public class Erased<T> {
private final int SIZE = 100;
public static void f(Object arg){
//无法编译
// if(arg instanceof T){}
// T var = new T();
// T[] array = new T[SIZE];
}
}
但是却可以利用isInstance匹配类型参数
public class ClassTypeCapture<T> {
Class<T> kind;
public ClassTypeCapture(Class<T> kind){
this.kind = kind;
}
public boolean f(Object arg){
return kind.isInstance(arg);
}
public static void main(String[] args) {
ClassTypeCapture<Building> ctt1 =
new ClassTypeCapture<Building>(Building.class);
System.out.println(ctt1.f(new Building()));
System.out.println(ctt1.f(new House()));
ClassTypeCapture<House> ctt2 =
new ClassTypeCapture<House>(House.class);
System.out.println(ctt2.f(new Building()));
System.out.println(ctt2.f(new House()));
}
}
//true
//true
//false
//true
像Erased上面,我们不能通过这样的方式去创建一个对象,T var = new T()这种在第11个例子里面有替代方法
而数组,我们需要注意的是Integer跟Integer[]是不一样的对象,当我们关联到擦除
//持有array的list
public class ListOfGenerics<T> {
private List<T> array = new ArrayList<T>();
public void add(T item){
array.add(item);
}
public T get(int index){
return array.get(index);
}
public List<T> rep(){
return array;
}
}
public class Generic<T> {
}
public class ArrayOfGeneric {
static final int SIZE = 100;
static Generic<Integer>[] gia;
@SuppressWarnings("unchecked")
public static void main(String[] args) {
//这里能获得数组的行为,他并没有形如T[]这样子的形式,所有放在List<T>里面
ListOfGenerics<Integer> ll = new ListOfGenerics<Integer>();
ll.add(1);
ll.add(2);
Integer i = ll.get(0);
System.out.println(i);
List<Integer> list = ll.rep();
System.out.println(list.get(0)+" "+list.get(1));
//ClassCastException
//gia = (Generic<Integer>[]) new Object[SIZE];
gia = (Generic<Integer>[])new Generic[SIZE];
System.out.println(gia.getClass().getSimpleName());//Generic[]
gia[0] = new Generic<Integer>();
System.out.println(gia[0]);//test17.Generic@30e79eb3
//很明显这不是Integer[]的对象,而是一个Generic数组对象,与List不一样
//编译错误
//gia[1] = new Object();
//gia[2] = new Generic<Double>();
}
}
这是利用list来获取数组的行为,但是真正关联到数组呢?
我们看看另外几种持有数组的方式:
(1)、T[] array;
public class GenericArray<T> {
private T[] array;
@SuppressWarnings("unchecked")
public GenericArray(int sz){array = (T[]) new Object[sz];}
public void put(int index,T item){array[index] = item;}
public T get(int index){return array[index];}
public T[] rep(){return array;}
public static void main(String[] args) {
GenericArray<Integer> gai = new GenericArray<Integer>(10);
Object[] oa = gai.rep();
System.out.println(oa.getClass().getSimpleName());//Object[]
//除非是确切的类型,Integer而不是Integer[],泛型并不会识别
//Integer[] i = (Integer[])gai.rep();//ClassCastException
//擦除:数组实际运行的是Object[]
//这里能运行,这里很确切地告诉了你这是Integer
gai.put(0, 1);
Integer ii = gai.get(0);
System.out.println(ii);//1
}
}
(2)、private Object[] array;
public class GenericArray2<T> {
private Object[] array;
@SuppressWarnings("unchecked")
public GenericArray2(int sz){array = (T[]) new Object[sz];}
public void put(int index,T item){array[index] = item;}
@SuppressWarnings("unchecked")
public T get(int index){return (T) array[index];}
@SuppressWarnings("unchecked")
public T[] rep(){return (T[]) array;}
public static void main(String[] args) {
GenericArray2<Integer> gai = new GenericArray2<Integer>(10);
for(int i= 0;i<10;i++){
gai.put(i, i);
}
for(int i=0;i<10;i++){
System.out.print(gai.get(i)+" ");
}
System.out.println();
try {
Integer[] ia = gai.rep();
//如果把返回类型改为Object[],同时在这里强转类型,结果仍然是异常
} catch (Exception e) {
System.out.println(e);
}
//ClassCastException这里仍然会报
//把T[]当作OBJECT处理的优势
//我们不太可能忘记数组的运行时类型,从而意外的引入缺陷,尽管大多数也可能是所有这类缺陷都可以在运行时快速的探测到
}
}
除了上面用list来解决,这里存在一种解决方案
newInstance是推荐的创建数组方式
public class GenericArrayWithTypeToken<T> {
private T[] array;
@SuppressWarnings("unchecked")
public GenericArrayWithTypeToken(Class<T> type,int sz) {
//这种方式是使用Array.newInstance,程序能正常运行
array = (T[]) Array.newInstance(type, sz);
}
public void put(int index,T item){
array[index] = item;
}
public T get(int index){
return array[index];
}
public T[] rep(){
return array;
}
public static void main(String[] args) {
GenericArrayWithTypeToken<Integer> gai = new GenericArrayWithTypeToken<Integer>(Integer.class, 10);
Integer[] ia = gai.rep();
}
}
13、边界:对象进入和离开方法的起点,编译器在编译期执行类型检查并插入转型代码的地点,在泛型中的所有动作的发生在边界处——对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型。边界就是发生动作的地方,按照笔者通俗的理解,形如<? extends xxx>或者<? super XX>的可以理解为边界
比如:List<? extends Animal>表示Animal是这个类型的上界,他可能是List<Cat>、List<Dog>,除了知道他是一个包括Animal的以及Animal子类,具体类型是不知道的
public void testAdd(List<? extends Animal> list){
//....其他逻辑
list.add(new Animal("animal"));
list.add(new Bird("bird"));
list.add(new Cat("cat"));
}
他可能是List<Cat>、也可以是List<Dog>、也可以是List<Animal>,如果是List<Dog>,那么添加Animal自然是不合法的
所以:
不能往List<? extends Pet> 添加任意对象,除了null
他不能添加对象,因为无法知道具体类型,但是能运用在可以确定的方法调用中:
如果act中参数不是List<? extends Animal> list而是List<Animal> list,编译器并不能识别List<Cat>,因为List<Animal>的类型是确定的,即Animal,通过添加上界,辨识传进来的是Animal的子类,于是可以使用List<Cat>
public class AnimalTrainer {
public void act(List<? extends Animal> list) {
for (Animal animal : list) {
animal.eat();
}
}
}
public class TestAnimal {
public static void main(String[] args) {
AnimalTrainer animalTrainer = new AnimalTrainer();
//Test 1
List<Animal> animalList = new ArrayList<>();
animalList.add(new Cat("cat1"));
animalList.add(new Bird("bird1"));
animalTrainer.act(animalList); //可以通过编译
//Test 2
List<Cat> catList = new ArrayList<>();
catList.add(new Cat("cat2"));
catList.add(new Cat("cat3"));
animalTrainer.act(catList); //也可以通过编译
}
}
G<? extends Y> 是 G<? extends X>的子类型(如List<? extends Cat> 是 List<? extends Animal>的子类型)。
G<X> 是 G<? extends X>的子类型(如List<Animal> 是 List<? extends Animal>的子类型)
G<?> 与 G<? extends Object>等同,如List<?> 与List<? extends Objext>等同。
这里的子类型不妨用“满足条件”去理解,比如第二条,可以理解为:满足G<X>的类型必定满足G<? extends X>
这也是通配符的应用,除了extends,通配符还可以是super,形如:List<? super Apple>,说明他的下界为Apple
那么对于下界:这个与上界不一样的地方在于
public void testAdd(List<? super Bird> list){
list.add(new Bird("bird"));
list.add(new Magpie("magpie"));
}
这种编译合法,因为编译器认为list的参数是Bird的父类,所有包括Bird的子类都是可以添加,但不能添加Bird的父类,同理是因为无法辨别是哪一个父类
G<? super X> 是 G<? super Y>的子类型(如List<? super Animal> 是 List<? super Bird>的子类型)。
G<X> 是 G<? super X>的子类型(如List<Animal> 是 List<? super Animal>的子类型)
无界通配符:只有一个?,如List<?>,同样不能往里面添加对象
参考:http://www.linuxidc.com/Linux/2013-10/90928p2.htm以下是展示了边界的基本要素的例子:
public interface HasColor {
java.awt.Color getColor();
}
public interface Weight {
int weight();
}
public class Dimension {
public int x,y,z;
}
public class Colored<T extends HasColor> {
T item;
Colored(T item){this.item = item;}
public T getItem() {
return item;
}
java.awt.Color color(){return item.getColor();}
}
public class ColoredDimension<T extends Dimension & HasColor> {
T item;
ColoredDimension(T item) {
this.item = item;
}
T getItem(){return item;}
java.awt.Color color(){return item.getColor();}
int getX(){return item.x;}
int getY(){return item.y;}
int getZ(){return item.z;}
}
public class Solid<T extends Dimension & HasColor &Weight>{
T item;
public Solid(T item) {
this.item = item;
}
java.awt.Color color(){return item.getColor();}
int getX(){return item.x;}
int getY(){return item.y;}
int getZ(){return item.z;}
int weight(){return item.weight();}
}
public class Bounded extends Dimension implements HasColor, Weight {
@Override
public int weight() {
return 0;
}
@Override
public Color getColor() {
return null;
}
}
public class BasicBounds {
public static void main(String[] args) {
Solid<Bounded> solid = new Solid<Bounded>(new Bounded());
solid.color();
solid.getY();
solid.weight();
}
}
(1)改写例子,在继承层次上消除冗余:
public class HoldItem<T> {
T item;
public HoldItem(T item) {
this.item = item;
}
T getItem(){return item;}
}
public class Colored2<T extends HasColor> extends HoldItem<T> {
public Colored2(T item) {
super(item);
}
java.awt.Color color(){return item.getColor();}
}
public class ColoredDimension2<T extends Dimension & HasColor> extends Colored2<T> {
public ColoredDimension2(T item) {
super(item);
}
int getX(){return item.x;}
int getY(){return item.y;}
int getZ(){return item.z;}
}
public class Solid2<T extends Dimension & HasColor &Weight> extends ColoredDimension2<T>{
Solid2(T item) {
super(item);
}
java.awt.Color color(){return item.getColor();}
int weight(){return item.weight();}
}
public class InheritBounds {
public static void main(String[] args) {
Solid2<Bounded> solid2 = new Solid2<Bounded>(new Bounded());
solid2.color();
solid2.getY();
solid2.weight();
}
}
(2)改写例子,在继承层次上消除冗余:
public interface SuperPower {
}
public interface XRayVision extends SuperPower {
void seeThroughWalls();
}
public interface SuperHearing extends SuperPower {
void hearSubtleNoises();
}
public interface SuperSmell extends SuperPower {
void trackBySmell();
}
public class SuperHero<POWER extends SuperPower>{
POWER power;
public SuperHero(POWER power) {
this.power = power;
}
POWER getPower(){return power;}
}
public class SuperSleuth<POWER extends XRayVision> extends SuperHero<POWER> {
public SuperSleuth(POWER power) {
super(power);
}
void see(){power.seeThroughWalls();}
}
public class CanineHero<POWER extends SuperHearing & SuperSmell> extends SuperHero<POWER> {
public CanineHero(POWER power) {
super(power);
}
void hear(){power.hearSubtleNoises();}
void smell(){power.trackBySmell();}
}
public class SuperHearSmell implements SuperHearing, SuperSmell {
@Override
public void trackBySmell() {
// TODO Auto-generated method stub
}
@Override
public void hearSubtleNoises() {
// TODO Auto-generated method stub
}
}
public class DogBoy extends CanineHero<SuperHearSmell> {
public DogBoy() {
super(new SuperHearSmell());
// TODO Auto-generated constructor stub
}
}
public class EpicBattle {
static <POWER extends SuperHearing>
void useSuperHearing(SuperHero<POWER> hero){
hero.getPower().hearSubtleNoises();
}
static <POWER extends SuperHearing & SuperSmell>
void superFind(SuperHero<POWER> hero){
hero.getPower().hearSubtleNoises();
hero.getPower().trackBySmell();
}
public static void main(String[] args) {
DogBoy dogBoy = new DogBoy();
useSuperHearing(dogBoy);
superFind(dogBoy);
}
}
14、混型:混合多个类的能力
(1)、接口产生混型效果:
public interface TimeStamped {
public long getStamp();
}
public class TimeStampedImp implements TimeStamped {
private final long timeStamped;
public TimeStampedImp(){
timeStamped = new Date().getTime();
}
public long getStamp(){
return timeStamped;
}
}
public interface SerialNumbered {
long getSeriaNumber();
}
public class SerialNumberedImpl implements SerialNumbered{
private static long counter = 1;
private final long seriaNumber = counter++;
@Override
public long getSeriaNumber() {
return seriaNumber;
}
}
public interface Basic {
public void set(String val);
public String get();
}
public class BasicImp implements Basic{
private String value;
public void set(String val){value = val;}
@Override
public String get() {
return value;
}
}
public class Mixin extends BasicImp implements TimeStamped,SerialNumbered{
private TimeStamped timeStamp = new TimeStampedImp();
private SerialNumbered serialNumber = new SerialNumberedImpl();
public long getStamp(){return timeStamp.getStamp();}
@Override
public long getSeriaNumber() {
return serialNumber.getSeriaNumber();
}
}
public class Mixins {
public static void main(String[] args) {
Mixin mixin1 = new Mixin();
Mixin mixin2 = new Mixin();
mixin1.set("test String 1");
mixin2.set("test String 2");
System.out.println(mixin1.get()+" "+mixin1.getStamp()+" "+mixin1.getSeriaNumber());
System.out.println(mixin2.get()+" "+mixin2.getStamp()+" "+mixin2.getSeriaNumber());
}
}
//test String 1 1468222102751 1
//test String 2 1468222102767 2
(2)、装饰器模式产生混型:
public class Basic {
private String value;
public void set(String val){
value = val;
}
public String get(){
return this.value;
}
}
public class Decorator extends Basic {
protected Basic basic;
public Decorator(Basic basic){this.basic = basic;}
public void set(String val){basic.set(val);}
public String get(){return basic.get();}
}
public class TimeStamped extends Decorator{
private final long timeStamp;
public TimeStamped(Basic basic) {
super(basic);
timeStamp = new Date().getTime();
}
public long getStamp(){return timeStamp;}
}
public class SerialNumbered extends Decorator{
private static long counter = 1;
private final long serialNumber = counter++;
public SerialNumbered(Basic basic) {
super(basic);
}
public long getSeriaNumber(){
return serialNumber;
}
}
public class Decoration {
public static void main(String[] args) {
TimeStamped t = new TimeStamped(new Basic());
TimeStamped t2 = new TimeStamped(new SerialNumbered(new Basic()));
SerialNumbered s = new SerialNumbered(new Basic());
SerialNumbered s2 = new SerialNumbered(new TimeStamped(new Basic()));
}
}
public class MixinProxy implements InvocationHandler {
Map<String,Object> delegatesByMethod;
public MixinProxy(TwoTuple<Object,Class<?>>... pairs) {
delegatesByMethod = new HashMap<String, Object>();
for(TwoTuple<Object, Class<?>> pair:pairs){
for(Method method :pair.second.getMethods()){
String methodName = method.getName();
if(!delegatesByMethod.containsKey(methodName))
delegatesByMethod.put(methodName, pair.first);
}
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
Object delegate = delegatesByMethod.get(methodName);
return method.invoke(delegate, args);
}
public static Object newInstance(TwoTuple... pairs) {
Class[] interfaces = new Class[pairs.length];
for(int i = 0;i<pairs.length;i++){
interfaces[i] = (Class) pairs[i].second;
}
ClassLoader cl = pairs[0].first.getClass().getClassLoader();
return Proxy.newProxyInstance(cl, interfaces, new MixinProxy(pairs));
}
}
public class DynamicProxyMixin {
public static void main(String[] args) {
Object mixin = MixinProxy.newInstance(
Tuple.tuple(new BasicImp(), Basic.class),
Tuple.tuple(new TimeStampedImp(), TimeStamped.class),
Tuple.tuple(new SerialNumberedImpl(), SerialNumbered.class));
Basic b = (Basic) mixin;
TimeStamped t = (TimeStamped) mixin;
SerialNumbered s = (SerialNumbered) mixin;
b.set("Hello");
System.out.println(b.get());
System.out.println(t.getStamp());
System.out.println(s.getSeriaNumber());
}
}