[英]Understanding upper and lower bounds on ? in Java Generics
我真的很难理解通配符参数。 我有几个问题。
?
作为类型参数只能在方法中使用。 例如: printAll(MyList<? extends Serializable>)
我不能用?
作为类型参数。
我了解上限?
. printAll(MyList<? extends Serializable>)
手段:“ printAll
将打印MyList
如果它具有实施该对象Serialzable
接口”。
我对super
有点问题。 printAll(MyList<? super MyClass>)
手段:“ printAll
将打印MyList
如果它具有的对象MyClass
或延伸的任何类MyClass
(的后代MyClass
)”。
纠正我哪里出错了。
简而言之,只有T
或E
或K
或V
或N
可以用作定义泛型类的类型参数。 ?
只能在方法中使用
public void printAll(MyList<? super MyClass>){ // code code code }
根据 Ivor Horton 的书MyList<? super MyClass>
MyList<? super MyClass>
表示如果MyList
具有MyClass
对象或它实现的任何接口或类,我可以打印它。 也就是说, MyClass
是一个下界。 它是继承层次结构中的最后一个类。 这意味着我最初的假设是错误的。
所以,假设MyClass
看起来像:
public class MyClass extends Thread implements ActionListener{ // whatever }
然后, printAll()
将打印如果
1.列表中有MyClass
对象
2.有对象Thread
或ActionListener
在List
因此,在阅读了该问题的许多答案后,这是我的理解:
? extends T
? extends T
表示任何扩展T
类。 因此,我们指的是T
的孩子。 因此, T
是上限。 继承层次结构中最上层的类
? super T
? super T
指任何类/接口是super
的T
。 因此,我们指的是T
所有父母。 因此, T
是下限。 继承层次结构中最底层的类
?
作为类型参数只能在方法中使用。 例如:printAll(MyList<? extends Serializable>)
我不能用?
作为类型参数。
通配符 ( ?
) 不是正式的类型参数,而是可以用作类型参数。 在你给出的例子中, ? extends Serializable
? extends Serializable
被给定为一个类型参数的一般类型MyList
,所述的printAll
方法的参数。
static <T extends Serializable> void printAll(MyList<T> myList)
我了解上限
?
.printAll(MyList<? extends Serializable>)
表示如果具有实现 Serialzable 接口的对象,printAll 将打印 MyList
更准确地说,它意味着一个呼叫printAll
如果它传递一个只会编译MyList
有一些通用的类型,它是或实现Serializable
。 在这种情况下,它将接受MyList<Serializable>
、 MyList<Integer>
等。
我对
super
有点问题。printAll(MyList<? super MyClass>)
表示printAll 将打印 MyList 如果它有 MyClass 的对象或任何扩展 MyClass 的类( MyClass的后代)
以super
为界的通配符是下界。 因此,我们可以说一个呼叫printAll
如果它传递一个只会编译MyList
有一些通用的类型为MyClass
或一些超级型MyClass
。 因此,在这种情况下,它将接受MyList<MyParentClass>
MyList<MyClass>
,例如MyList<MyParentClass>
或MyList<MyParentClass>
MyList<Object>
。
所以,假设 MyClass 看起来像:
public class MyClass extends Thread implements ActionListener{ // whatever }
然后,printAll() 将打印如果
- 列表中有 MyClass 的对象
- 列表中有 Thread 或 ActionListener 的对象
你在正确的轨道上。 但是我认为说例如“如果列表中有MyClass
对象,它就会打印”是有问题的。 这听起来像是在定义运行时行为 - 泛型都是关于编译时检查的。 例如,不能将MyList<MySubclass>
作为参数传递给MyList<MySubclass>
MyList<? super MyClass>
MyList<? super MyClass>
,即使它可能包含MyClass
实例,通过继承。 我将其改写为:
对printAll(MyList<? super MyClass>)
的调用只有在传递了以下参数时才会编译:
MyList<MyClass>
MyList<Thread>
MyList<Runnable>
MyList<ActionListener>
MyList<EventListener>
MyList<Object>
MyList<? super X>
MyList<? super X>
其中X
是MyClass
、 Thread
、 Runnable
、 ActionListener
、 EventListener
或Object
。因此,在阅读了该问题的许多答案后,这是我的理解:
? extends T
? extends T
表示任何扩展 T 的类。 因此,我们指的是 T 的孩子。因此, T 是上限。 继承层次结构中最上层的类
? super T
? super T
指任何类/接口是super
T.因此我们指的是所有T. T的父母因此下界。 继承层次结构中最底层的类
关闭,但我不会说“ T
孩子”或“ T
父母”,因为这些界限是包含性的——说“ T
或其子类型”和“ T
或其超类型”会更准确。
首先, T
或E
或K
或其他任何不是固定名称。 它们只是类型变量,由您决定它们的名称。 T
、 E
、 K
只是示例,但您可以将其称为Foo
或其他名称。
现在去你的第一个问题:因为通配符?
表示“任何和未知”类型,未指定的类型,在未指定的类型上声明泛型类没有任何意义。 当您不关心类型时,在方法的参数或变量中使用通配符很有用。
现在关于您的第二个问题:下限为您的通用方法提供了更大的灵活性。 extends
和super
都是相反的:
? extends T
? extends T
:未知类型,它是T
的子类型? super T
? super T
:未知类型,它是T
的超类型当您想要接受与 T 兼容的类型(以便 T 是该类型)时,后者会很有用。 可以在此处找到一个实际示例。
让我们从头开始。
严格来说,任何有效的 java 标识符都可以用作泛型类型参数——它只是一种特殊类型的变量:
public static final class MyGenericClass<MyGenericType> {
}
是完全有效的 Java。
接下来,您可以使用?
任何可以声明的地方。 您可以在声明变量时使用通配符,但不能在实例化它们时使用:
public static final class MyGenericClass {
private final Collection<? extends String> myThings;
public MyGenericClass(Collection<? extends String> myThings) {
this.myThings = myThings;
}
public void doStuff(final Collection<? extends String> myThings) {
}
}
再次全部有效,您不能这样做:
final Collection<? extends String> myThings = new ArrayList<? extends String>();
当涉及到extends
与super
这被称为协方差与逆变。 它确定允许提供的类型沿类层次结构的哪个方向行进:
final Collection<? extends Runnable> example1 = new ArrayList<Runnable>();
final Collection<? extends Runnable> example2 = new ArrayList<TimerTask>();
final Collection<? super Runnable> example3 = new ArrayList<Runnable>();
final Collection<? super Runnable> example4 = new ArrayList<Object>();
前两个示例演示了extends
- 您可以从Collection
假设的最紧密的界限是Runnable
因为用户可以传递在其继承层次结构中具有Runnable
的任何东西的Collection
。
第二个两个示例演示super
-最严格的约束,你可以从假设Collection
的Object
,因为我们允许任何处于继承层次Runnable
。
对于第一个问题:你不能用?
作为类型参数。 以下不会编译:
void <?> foo() {}
?
用于绑定到另一个泛型而不提供类型参数。 您可以为方法编写:
void foo(List<?> e) {}
你也可以为类编写:
public class Bar<E extends List<?>> { }
对于super
的使用:
public void printAll(MyList<? super MyClass>){
// code code code
}
这不会像你说的那样打印列表“如果它有 MyClass 的对象”。 它可以拥有作为 MyClass 父类的子类的任何类的对象。 编译器在编译时并不知道列表中的对象是什么。
为了解决这个问题,请考虑一个带有Number
类层次结构的简单示例。 Float
和Integer
是Number
孩子。 你可以这样写你的方法:
public void printAll(List<? super Float>){
// code code code
}
然后您可以使用List<Number>
调用该方法:
List<Number> numbers = new ArrayList<>();
numbers.add(1); // actually only add an Integer
printAll(numbers); // compiles.
在这种情况下,这可能不会非常有用。 例如,当您想将Float
添加到集合而不希望它只是一个列表时,它会很有用,例如:
public void addFloat(List<? super Float> list){
list.add(2.5);
}
下限表示:您可以使用在 'super' 关键字之后提到的类,或其任何超类型。 这可能会变得棘手。 这些超类型可能有其他(完全不同的)类继承自它们,如下例所示。
说,
List<? super Car> cars = new ArrayList <Vehicle>();
是否允许程序员编写:
cars.add(new Helicopter()); //Helicopter is a kind of Vehicle
这显然不应该被允许,它反映了使用下限的危险。
应该允许程序员将车辆添加到列表中,但不能添加任何车辆。 他必须投射它,让 Java 知道他毕竟只是添加了一辆 Car Vehicle,就像这样:
cars.add((Car) new Vehicle());
嗯,您对super
( printAll(MyList<? super MyClass>)
( printAll(MyList<? super MyClass>)
) 的声明不清楚。 这意味着,假设 Myclass 扩展Object
是你可以printAll(MyList<MyClass>)
并且你可以printAll(MyList<Object>)
但没有别的......这意味着printAll(MyList<Object>)
的泛型类型必须是一个超类(不是 MyClass 的子类)。 这和你说的不一样。
至于 T、E、K、V 或 N,好吧,它们本身就是毫无意义的名称。 你可以使用任何你想要的东西。 约定建议使用单字母大写值,T 通常用于泛型方法,E 用于类......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.