给出一个简单的课程......
public class Parent {
private final String value;
public Parent(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
......及其子类,......
public class Child extends Parent {
public Child(String value) {
super(value);
}
}
......以下程序编译并执行时没有错误或警告:
public class Main {
public static void main(String[] args) {
List<? extends Parent> items = getItems();
for (Parent p : items) {
System.out.println(p.toString());
}
}
private static List<? extends Parent> getItems() {
List<Parent> il = new ArrayList<Parent>();
il.add(new Parent("Hello"));
il.add(new Child("World"));
return il;
}
}
但是 getItems()
的返回类型怎么样?我觉得语义错误,因为函数的结果包含{{1} }
答案 0 :(得分:4)
List<A>
是不变的。 List<? extends A>
是协变的,List<? super A>
是逆变的。
协变类型无法在没有强制转换的情况下进行修改(这是有道理的,因为可变集合本质上不是协变的。)
如果不需要修改集合,则没有理由不使用List<? extends A>
来获得适当的子类型关系(例如List<? extends Integer>
是List<? extends Number>
的子类型})。
我不建议将此视为&#34;写保护&#34;因为演员来规避它是微不足道的。只需使用最常用的类型即可。如果你正在编写一个接受List<A>
参数的方法,并且该方法永远不需要修改列表,那么List<? extends A>
是更通用的,因此更合适的类型。
作为返回类型,使用协方差也很有用。假设您正在实现一个返回类型为List<Number>
的方法,并且您想要返回List<Integer>
。你在阿尔卑斯山中被发现是一个陌生人:无论是复制它还是做一个丑陋的演员。但是如果你的类型是正确的,并且界面需要List<? extends Number>
,那么你可以返回你拥有的东西。
答案 1 :(得分:3)
你已经提到了#34;写保护&#34;作为一种好处,取决于使用场景,可能有用或无用。
另一个好处是一些灵活性,从某种意义上说,你可以改变方法的实现并按照
的方式写一些东西。 List<Child> il = new ArrayList<Child>();
il.add(new Child("Hello"));
il.add(new GrandChild("World")); // assume there is such a thing
List<? extends Parent> result = il;
return result;
确实会更改列表内容的类型,但它会使接口(读取:公共方法签名)保持不变。例如,如果您编写框架,这一点至关重要。
答案 2 :(得分:2)
当您开始在子类中使用专门的? extends X
时,? super Y
和List
语法会派上用场,您需要确保不会混入任何无效对象。 ArrayList
你可以随时冒险进行未经检查的演员并绕过限制,但也许这个例子说明了你可以使用参数化的方式:
import java.util.AbstractList;
import java.util.List;
/**
* http://stackoverflow.com/q/25617284/1266906
*/
public class ListTest {
public static void main(String[] args) {
ParentList parents = new ParentList(new Parent("one"), new Child("two"));
List<? extends Parent> parentsA = parents; // Read-Only, items assignable to Parent
List<? super Parent> parentsB = parents; // Write-Only, input of type Parent
List<Parent> parentC = parents; // Valid as A and B were valid
List<? super Child> parentD = parents; // Valid as you can store a Child
// List<? extends Child> parentE = parents; // Invalid as not all contained values are Child
ChildList children = new ChildList(new Child("one"), new Child("two"));
List<? extends Parent> childrenA = children; // Read-Only, items assignable to Parent
// List<? super Parent> childrenB = children; // Invalid as ChildList can not store a Parent
List<? super Child> childrenC = children; // Write-Only, input of type Child
// List<Parent> childrenD = children; // Invalid as B was invalid
List<Child> childrenE = children;
}
public static class ChildList extends AbstractList<Child> {
private Child[] values;
public ChildList(Child... values) {
this.values = values;
}
@Override
public Child get(int index) {
return values[index];
}
@Override
public Child set(int index, Child element) {
Child oldValue = values[index];
values[index] = element;
return oldValue;
}
@Override
public int size() {
return values.length;
}
}
public static class ParentList extends AbstractList<Parent> {
private Parent[] values;
public ParentList(Parent... values) {
this.values = values;
}
@Override
public Parent get(int index) {
return values[index];
}
@Override
public Parent set(int index, Parent element) {
Parent oldValue = values[index];
values[index] = element;
return oldValue;
}
@Override
public int size() {
return values.length;
}
}
public static class Parent {
private final String value;
public Parent(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
public static class Child extends Parent {
public Child(String value) {
super(value);
}
}
}
? extends X
或任何延伸X
&#39;的类型阅读X
或者&#39;可分配给X
&#39; ? super X
或任何类型X
扩展&#39;或者&#39;我可以将X
类型的变量分配给&#39;。答案 3 :(得分:0)
当您说List<? extends Parent>
时,它表示Parent
Type或其任何子项的任何对象。我同意这有点误导,但这是它的工作方式。
我可以从这种模式中看到的一个潜在好处是在main
函数中使用反射API。
假设您有函数返回List<Parent>
,并且在main()
中,如果您执行
private static List<Parent> getItems() { ... }
public static void main(String[] args) {
List<? extends Parent> items = getItems();
for (Parent p : items) {
System.out.println(p.getClass().getSimpleName());
}
}
会让你
Parent
Parent
好像你有
private static List<? extends Parent> getItems() { ... }
主要打印
Parent
Child