了解上限和下限? 在Javagenerics中
我真的很难理解外卡参数。 我有几个问题。
-
?
作为类型参数只能在方法中使用。 例如:printAll(MyList)
我无法定义类?
作为类型参数。 -
我明白了上限
?
。printAll(MyList)
表示:“printAll
将打印printAll
如果它具有实现Serialzable
接口的对象。 ”
我对super
有点问题。printAll(MyList)
表示:“printAll
将打印printAll
如果它有MyClass
对象或任何扩展MyClass
类(MyClass
的后代)。 ”
在我出错的地方纠正我。
简而言之,只有T
或E
或K
或V
或N
可用作定义generics类的类型参数。 ?
只能用于方法
更新1:
public void printAll(MyList){ // code code code }
根据Ivor Horton的书MyList
MyList
意味着我可以打印MyList
如果它有MyClass
对象或它实现的任何接口或类。 也就是说, MyClass
是一个下限 。 它是inheritance层次结构中的最后一个类。 这意味着我最初的假设是错误的。
所以,如果MyClass
看起来像:
public class MyClass extends Thread implements ActionListener{ // whatever }
然后, printAll()
将打印if
1.列表中有MyClass
对象
2. List
有Thread
或ActionListener
对象
更新2:
所以,在阅读了问题的许多答案后,我的理解是:
-
? extends T
? extends T
表示任何扩展T
类 。 因此,我们指的是T
的孩子 。 因此,T
是上限。 inheritance层次结构中最上层的类 -
? super T
? super T
表示? super T
任何类/接口 。 因此,我们指的是T
所有父母 。 因此T
是下限。 inheritance层次结构中最低级的类
?
作为类型参数只能在方法中使用。 例如:printAll(MyList extends Serializable>)
我无法定义类?
作为类型参数。
通配符( ?
)不是forms类型参数,而是可以用作类型参数 。 在你给出的例子中, ? extends Serializable
? extends Serializable
作为printAll
方法参数的generics类型printAll
的类型参数给出。
方法也可以声明像类这样的类型参数 ,例如:
static void printAll(MyList myList)
我明白了上限
?
。printAll(MyList extends Serializable>)
表示如果printAll具有实现Serialzable接口的对象,则printAll将打印MyList
更准确地说,这意味着对printAll
的调用只有在传递printAll
才会编译 ,该printAll
具有一些或实现Serializable
generics类型 。 在这种情况下,它将接受MyList
, MyList
等。
我对
super
有点问题。printAll(MyList super MyClass>)
表示printAll将打印MyList,如果它有MyClass对象或任何扩展MyClass的类 ( MyClass的后代)
与super
绑定的通配符是下限 。 所以我们可以说printAll
的调用只有在传递printAll
才会编译,其中printAll
类型为MyClass
或MyClass
超类型。 所以在这种情况下它会接受MyList
MyList
,例如MyList
或MyList
MyList
。
所以,如果MyClass看起来像:
public class MyClass extends Thread implements ActionListener{ // whatever }
然后,printAll()将打印if
- 列表中有MyClass的对象
- 列表中有Thread或ActionListener对象
你走在正确的轨道上。 但我认为例如“如果列表中有MyClass
对象将会打印”是有问题的。 这听起来像是在定义运行时行为 – generics都是关于编译时检查的。 例如,无法将MyList
作为MyList
MyList super MyClass>
的参数传递MyList super MyClass>
MyList super MyClass>
,即使它可能包含inheritance的MyClass
实例。 我会改写它:
对printAll(MyList super MyClass>)
的调用只有在传递给它时才会编译:
-
MyList
-
MyList
-
MyList
-
MyList
-
MyList
-
MyList
-
MyList super X>
MyList super X>
其中X
是MyClass
,Thread
,Runnable
,ActionListener
,EventListener
或Object
。
所以,在阅读了问题的许多答案后,我的理解是:
? extends T
? extends T
表示任何扩展T的类 。 因此,我们指的是T的子女。因此, T是上界。 inheritance层次结构中最上层的类
? super T
? super T
表示任何? super T
类/接口。因此,我们指的是T. T的所有父类, 因此是下限。 inheritance层次结构中最低级的类
关闭,但我不会说“ T
孩子”或“ T
父母”,因为这些界限是包容性的 – 说“ T
或其子类型”和“ T
或其超类型”会更准确。
首先是T
或E
或K
或其他不是固定名称的。 它们只是类型变量,您可以决定它们的名称。 T
, E
, K
只是示例,但您可以将其称为Foo
或其他。
现在转到第一个问题:自从通配符开始?
表示“任何和未知”类型,未指定类型,在未指定类型上声明类通用没有任何意义。 当您不关心类型时,在方法参数或变量中使用通配符很有用。
现在关于你的第二个问题:下限为你的generics方法提供了更大的灵活性。 extends
和super
是相反的:
-
? extends T
? extends T
:一个未知类型,它是T
的子类型 -
? super T
? super T
:一种未知类型,是超级类型的T
当你想要接受一个与T兼容的类型(因此T是一个那种类型)时,后者会很有用。 这里可以找到一个实际的例子。
让我们从头开始。
严格地说, 任何有效的java标识符都可以用作generics类型参数 – 它只是一种特殊类型的变量:
public static final class MyGenericClass { }
是完全有效的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(); final Collection extends Runnable> example2 = new ArrayList(); final Collection super Runnable> example3 = new ArrayList(); final Collection super Runnable> example4 = new ArrayList
前两个示例演示了extends
– 您可以从Collection
的最严格的绑定是Runnable
因为用户可以在其inheritance层次结构中传递任何具有Runnable
的Collection
。
第二个示例演示了super
– 您可以从Collection
is Object
假设的最严格的边界,因为我们允许Runnable
的inheritance层次结构中的任何内容。
嗯你的super
声明( printAll(MyList super MyClass>)
( printAll(MyList super MyClass>)
)并不清楚。 这意味着,假设Myclass扩展了Object
,你可以printAll(MyList
,你可以printAll(MyList
但没有别的……这意味着printAll(MyList
的generics类型必须是超类(不是MyClass的子类。 这跟你说的不一样。
至于T,E,K,V或N,它们本身就是毫无意义的名称。 你可以使用你想要的任何东西。 惯例建议使用单字母大写值,T通常用于generics方法,E用于类….
对于第一个问题:你不能用?
定义一个方法?
作为类型参数。 以下内容无法编译:
void > foo() {}
?
用于绑定到另一个generics而不提供类型参数。 你可以写方法:
void foo(List> e) {}
你也可以写课:
public class Bar> { }
对于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
调用该方法:
List numbers = new ArrayList<>(); numbers.add(1); // actually only add an Integer printAll(numbers); // compiles.
在这种情况下,这可能不会非常有用。 例如,当您想要将Float
添加到集合而不希望它只是List时,它会有用,例如:
public void addFloat(List super Float> list){ list.add(2.5); }
?
也可以用作方法返回类型
List extends Number> xxx() { ... }
或作为字段或变量类型
List extends Number> xxx = ... List super Nember> xxx = ...