返回信息流附件(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
麻烦知道的讲解下?最好尽量详细些
这是一条镜像帖。来源:北邮人论坛 / java / #16649同步于 2010/12/2
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Java机器人发帖
关于反射与泛型
JacKie575
2010/12/2镜像同步18 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
这是在做静态检查时就不会过的错误,因为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能够兼容这个类型,不会报编译错误
【 在 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 这个结果?
实在是在这里有点混乱~
fruit.get().getClass().getSimpleName()已经是运行时刻的输出了,这个时候,fruit.get()已经确定为一个Apple类型,再进行getClass().getSimpleName(),自然会得到Apple的结果
关键是编译和运行的时候的不同
不过我有点不明白为什么不能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.*]
:
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可以编译通过,因为完全无法进行类型检查。这里会引发警告。
【 在 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/
:
1. 不平等。
? extends Fruit有不确定性,就是“不知道是哪个类型,所以都不敢用”。但是,任何子类型的对象都可以赋给这个变量。
2. 确实。即使换成WhiteFur,编译也通不过。
Covariance本来就很纠结的。这里这个set确实是没法用了。但是,如果还有别的方法,参数或者返回值没有covariant类型参数,仍然可以用。
这里还有更纠结的在第9节。
http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
我觉得
? extends Fruit 虽然可以匹配所有fruit的子类,但是匹配不上fruit本身。所以,在继承树上,? extends Fruit跟apple和orange是一层的,而不是他们的父类。我是这个意思,恩
【 在 wks (cloverprince) 的大作中提到: 】
: 1. 不平等。
: ? extends Fruit有不确定性,就是“不知道是哪个类型,所以都不敢用”。但是,任何子类型的对象都可以赋给这个变量。
: 2. 确实。即使换成WhiteFur,编译也通不过。
: ...................
? 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) 的大作中提到: 】
: ...................