BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / java / #16649同步于 2010/12/2
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Java机器人发帖

关于反射与泛型

JacKie575
2010/12/2镜像同步18 回复
附件(823B) CovariantArrays.java 附件(1KB) Holder.java //: generics/CovariantArrays.java class Fruit {} class Apple extends Fruit {} class Jonathan extends Apple {} class Orange extends Fruit {} public class CovariantArrays { public static void main(String[] args) { Fruit[] fruit = new Apple[10]; fruit[0] = new Apple(); // OK fruit[1] = new Jonathan(); // OK // Runtime type is Apple[], not Fruit[] or Orange[]: try { // Compiler allows you to add Fruit: fruit[0] = new Fruit(); // ArrayStoreException } catch(Exception e) { System.out.println(e); } try { // Compiler allows you to add Oranges: fruit[0] = new Orange(); // ArrayStoreException } catch(Exception e) { System.out.println(e); } } } /* Output: java.lang.ArrayStoreException: Fruit java.lang.ArrayStoreException: Orange *///:~ //: generics/Holder.java public class Holder<T> { private T value; public Holder() {} public Holder(T val) { value = val; } public void set(T val) { value = val; } public T get() { return value; } public boolean equals(Object obj) { return value.equals(obj); } public static void main(String[] args) { Holder<Apple> Apple = new Holder<Apple>(new Apple()); Apple d = Apple.get(); Apple.set(d); // Holder<Fruit> Fruit = Apple; // Cannot upcast Holder<? extends Fruit> fruit = Apple; // OK Fruit p = fruit.get(); System.out.println(fruit.get().getClass().getSimpleName()); d = (Apple)fruit.get(); // Returns 'Object' try { Orange c = (Orange)fruit.get(); // No warning } catch(Exception e) { System.out.println(e); } // fruit.set(new Apple()); // Cannot call set() // fruit.set(new Fruit()); // Cannot call set() System.out.println(fruit.equals(d)); // OK } } /* Output: (Sample) java.lang.ClassCastException: Apple cannot be cast to Orange true *///:~ Holder.java中, System.out.println(fruit.get().getClass().getSimpleName()); 输出结果是 Apple;所以我认为 fruit.get() 返回Apple; 于是我想把下面这行的(Apple)去掉: d = (Apple)fruit.get(); 改成: d = fruit.get(); 但是编译会报错,类型不匹配:不能从 capture#4-of ? extends Fruit 转换为 Apple 麻烦知道的讲解下?最好尽量详细些
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
yueyingcs机器人#1 · 2010/12/2
这是在做静态检查时就不会过的错误,因为get方法返回了一个泛型,所以在静态编译过程中,要按照T的静态名字进行匹配,fruit本身传入的是? extends Fruit,所以fruit的get方法的返回类型静态就写为 ? extends Fruit,所以如果令d=fruit.get(),则必须d的类型要兼容 ? extends Fruit,由于d的类型声明为Apple是确定的,而fruit.get()的类型现在还只是? extends Fruit,即有可能是Fruit也有可能是Fruit的子类Apple,没法确定只有在运行时刻才能知道。这时这两种类型是不兼容的,所以会报编译错误。但是p=fruit.get()却没有问题,是因为? extends Fruit这个类型不会超过Fruit这个范围,所以p能够兼容这个类型,不会报编译错误
JacKie575机器人#2 · 2010/12/2
【 在 yueyingcs 的大作中提到: 】 : 这是在做静态检查时就不会过的错误,因为get方法返回了一个泛型,所以在静态编译过程中,要按照T的静态名字进行匹配,fruit本身传入的是? extends Fruit,所以fruit的get方法的返回类型静态就写为 ? extends Fruit,所以如果令d=fruit.get(),则必须d的类型要兼容 ? extends Fruit,由于d的类型声明为Apple是确定的,而fruit.get()的类型现在还只是? extends Fruit,即有可能是Fruit也有可能是Fruit的子类Apple,没法确定只有在运行时刻才能知道。这时这两种类型是不兼容的,所以会报编译错误。但是p=fruit.get()却没有问题,是因为? extends Fruit这个类型不会超过Fruit这个范围,所以p能够兼容这个类型,不会报编译错误 : -- 是的,? extends Fruit 这个我大概有点了解了。但是我想知道的是 System.out.println(fruit.get().getClass().getSimpleName()); 为什么会输出Apple呢?如果按照行为的一致性,应该输出 ? extends Fruit 这个结果? 实在是在这里有点混乱~
yueyingcs机器人#3 · 2010/12/2
fruit.get().getClass().getSimpleName()已经是运行时刻的输出了,这个时候,fruit.get()已经确定为一个Apple类型,再进行getClass().getSimpleName(),自然会得到Apple的结果
ppooooll机器人#4 · 2010/12/3
关键是编译和运行的时候的不同 不过我有点不明白为什么不能set 【 在 JacKie575 (小五哥) 的大作中提到: 】 : : : //: generics/Holder.java : : public class Holder<T> { : private T value; : public Holder() {} : public Holder(T val) { value = val; } : public void set(T val) { value = val; } : public T get() { return value; } : public boolean equals(Object obj) { : return value.equals(obj); : } : public static void main(String[] args) { : Holder<Apple> Apple = new Holder<Apple>(new Apple()); : Apple d = Apple.get(); : Apple.set(d); : // Holder<Fruit> Fruit = Apple; // Cannot upcast : Holder<? extends Fruit> fruit = Apple; // OK : Fruit p = fruit.get(); : System.out.println(fruit.get().getClass().getSimpleName()); : d = (Apple)fruit.get(); // Returns 'Object' : try { : Orange c = (Orange)fruit.get(); // No warning : } catch(Exception e) { System.out.println(e); } : // fruit.set(new Apple()); // Cannot call set() : // fruit.set(new Fruit()); // Cannot call set() ~~~~~~~~~~~~~~~~~~~~~这个为啥不行啊? : System.out.println(fruit.equals(d)); // OK : } : } /* Output: (Sample) : java.lang.ClassCastException: Apple cannot be cast to Orange : true : *///:~ : : : : Holder.java中, : System.out.println(fruit.get().getClass().getSimpleName()); : 输出结果是 Apple;所以我认为 fruit.get() 返回Apple; : : 于是我想把下面这行的(Apple)去掉: : d = (Apple)fruit.get(); : 改成: : d = fruit.get(); : : 但是编译会报错,类型不匹配:不能从 capture#4-of ? extends Fruit 转换为 Apple : : 麻烦知道的讲解下?最好尽量详细些 : -- : : ※ 来源:·北邮人论坛 http://bbs.byr.cn·[FROM: 123.117.32.*] :
wks机器人#5 · 2010/12/3
class Main { public static void main(String[] args) { Holder<? extends Fruit> h = new Holder<Orange>(); // Correct h.set(new Fruit()); // ERROR: Argument type not acceptable } } h的类型是Holder<? extends Fruit>,所以Holder<Orange>是Holder<? extends Fruit>的一个子类。SpecialHolder也是 既然h可以指向Holder<Orange>,编译器怎么能在编译期确定h就能装Fruit呢? 再看看这个: package whitehorse; class Fur {} class BlackFur extends Fur {} class WhiteFur extends Fur {} class Horse<FurType extends Fur> { private FurType fur; public void setFur(FurType fur) { this.fur = fur; } } class BlackHorse extends Horse<BlackFur> {} class WhiteHorse extends Horse<WhiteFur> {} public class HorseMain { public static void main(String[] args) { Horse<? extends Fur> horse = new WhiteHorse(); horse.setFur(new BlackFur()); // ERROR! Horse horse2 = new WhiteHorse(); horse2.setFur(new BlackFur()); // WARNING } } 既然指定了horse的类型是Horse<? extends Fur>,那么horse就可以指向WhiteHorse。 所以,如果horse.setFur可以编译通过,那岂不是我们可以随便给白马加黑毛了? horse2可以编译通过,因为完全无法进行类型检查。这里会引发警告。
ppooooll机器人#6 · 2010/12/3
【 在 wks (cloverprince) 的大作中提到: 】 : 标 题: Re: 关于反射与泛型 : 发信站: 北邮人论坛 (Fri Dec 3 10:17:09 2010), 站内 : : class Main { : public static void main(String[] args) { : Holder<? extends Fruit> h = new Holder<Orange>(); // Correct : h.set(new Fruit()); // ERROR: Argument type not acceptable : } : } : : h的类型是Holder<? extends Fruit>,所以Holder<Orange>是Holder<? extends Fruit> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 的一个子类。SpecialHolder也是 ~~~~~~~~~~觉得Orange和 ? extends Fruit是平等级别的吧 : 既然h可以指向Holder<Orange>,编译器怎么能在编译期确定h就能装Fruit呢? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~那也就是说这个set函数根本就没法用嘛。如果指向apple,就不能是orange,如果是orange就不能是apple,所以set函数悲剧了。 : : 再看看这个: : package whitehorse; : : class Fur {} : class BlackFur extends Fur {} : class WhiteFur extends Fur {} : : class Horse<FurType extends Fur> { : private FurType fur; : public void setFur(FurType fur) { : this.fur = fur; : } : } : : class BlackHorse extends Horse<BlackFur> {} : class WhiteHorse extends Horse<WhiteFur> {} : : public class HorseMain { : public static void main(String[] args) { : Horse<? extends Fur> horse = new WhiteHorse(); : horse.setFur(new BlackFur()); // ERROR! ~~同样的道理,这个地方换成whitefur能行吗?同样编译时不能确定是白马还是黑马,换成whitefur也有可能不对啊 : : Horse horse2 = new WhiteHorse(); : horse2.setFur(new BlackFur()); // WARNING : } : } : : 既然指定了horse的类型是Horse<? extends Fur>,那么horse就可以指向WhiteHorse。 : 所以,如果horse.setFur可以编译通过,那岂不是我们可以随便给白马加黑毛了? : : horse2可以编译通过,因为完全无法进行类型检查。这里会引发警告。 : -- : 免费贴代码:http://wksprivate.appspot.com/pastebin/ : 人品计算器:http://wksprivate.appspot.com/renpincalc/ : 博客:http://cloverprince.javaeye.com/ :
wks机器人#7 · 2010/12/3
1. 不平等。 ? extends Fruit有不确定性,就是“不知道是哪个类型,所以都不敢用”。但是,任何子类型的对象都可以赋给这个变量。 2. 确实。即使换成WhiteFur,编译也通不过。 Covariance本来就很纠结的。这里这个set确实是没法用了。但是,如果还有别的方法,参数或者返回值没有covariant类型参数,仍然可以用。 这里还有更纠结的在第9节。 http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
ppooooll机器人#8 · 2010/12/3
我觉得 ? extends Fruit 虽然可以匹配所有fruit的子类,但是匹配不上fruit本身。所以,在继承树上,? extends Fruit跟apple和orange是一层的,而不是他们的父类。我是这个意思,恩 【 在 wks (cloverprince) 的大作中提到: 】 : 1. 不平等。 : ? extends Fruit有不确定性,就是“不知道是哪个类型,所以都不敢用”。但是,任何子类型的对象都可以赋给这个变量。 : 2. 确实。即使换成WhiteFur,编译也通不过。 : ...................
wks机器人#9 · 2010/12/3
? extends Fruit可以匹配Fruit本身。 class A<? extends Object> {} class B extends A<Object> {} 这是可以编译通过的。 Horse<? extends Fur> h3 = new Horse<Fur>(); 这个也可以编译。 但是h3.setFur(new Fur());还是不行 h3.setFur(new BlackFur()); h3.setFur(new WhiteFur()); 都不行。 【 在 ppooooll 的大作中提到: 】 : 我觉得 : ? extends Fruit 虽然可以匹配所有fruit的子类,但是匹配不上fruit本身。所以,在继承树上,? extends Fruit跟apple和orange是一层的,而不是他们的父类。我是这个意思,恩 : 【 在 wks (cloverprince) 的大作中提到: 】 : ...................