我对类型参数感到困惑。在下面的代码中,CollectionData尖括号内的类型可以称为类型参数。 ArrayList和Generator中的类型不是类型参数,在CollectionData中应该是相同的。如何理解?
//correct
public class CollectionData<T> extends ArrayList<T> {
CollectionData(Generator<T> generator) {
}
}
interface Generator<E> {
E next();
}
以下是错误的。但有时我无法帮助写这篇文章。
// wrong
public class CollectionData<T> extends ArrayList<E> {
CollectionData(Generator<W> generator) {
}
}
interface Generator<E> {
E next();
}
已添加:
在上面的示例中,类型变量只能在CollectionData的尖括号中定义,而不是在ArrayList或Generator中定义?然后ArrayList和CollectionData使用这些类型。正确?
答案 0 :(得分:3)
这些泛型类型只是类型变量。
书写
public class CollectionData<T> extends ArrayList<E> {
类似于写作
public int func(int x) {
return y;
}
如果没有变量y
,则此代码错误。
但由于您声明了x
,因此您可以在适当情况下使用x
:
public int func(int x) {
return x;
}
通用类型也是如此。 当你写
public class CollectionData<T> extends ArrayList<T>
你是说T
里面的ArrayList<T>
是相同的 T
,由你的类的泛型类型参数给出。
构造函数的参数类型也是如此。
所以,为了帮助你知道何时写出&#34;正确&#34;代码:如果您要求两个泛型类型相同(例如,为类提供的泛型类型,基类的泛型类型或函数的参数),则必须使用相同的类型变量。 当您需要存储在该变量中的确切值时,这与在函数内使用相同变量的情况相同。
泛型类型只能在类名上声明:
public class CollectionData<T> extends ArrayList<E>
声明T
,但只有使用 E
。当然,这要求E
已被声明为某种类型的泛型类型(或类)。 (当E
是泛型类型时,只有当CollectionData<T>
位于声明E
的外部类中时,这才有效。)
当然,也可以为每个函数声明泛型类型:
public <T> void foo(T x) { x.baz(); }
此声明 T
和阴影 T
的任何定义。
例如,在此上下文中
public class Foo<T> {
public <T> void foo(T x) { x.baz(); }
}
T
方法中的foo
不类T
声明的Foo
。
这又像阴影变量一样有效:
public class Foo {
int x;
public int foo(int x) {
// returns x from argument, not the field of the class
return x;
}
}
答案 1 :(得分:1)
我将假设您正在尝试使用构造函数中CollectionData
传入的内容填充Generator
个实例。
需要匹配时,需要使用相同的类型参数。在您的第一个示例中,CollectionData
中的所有类型参数均为T
。由于它们具有相同的标识符,因此编译器知道它们必须匹配。
在第二个示例中,您有类型参数T
,W
和E
。这不起作用,因为您无法使用E
的实例填充T
或W
的列表。编译器无法知道实例类型是否兼容。
答案 2 :(得分:1)
我认为导致你的混淆的是定义泛型类型和使用之间的区别。
定义泛型类型基本上就像定义一个函数 - 你定义它的参数以及它如何使用它的参数。
使用泛型类型意味着它必须完全可以被编译器理解 - 它不能依赖于尚未定义的类型。
此处,在您的示例中,定义以单词extends
结尾。接下来的所有内容都使用泛型类型,因此除了定义标题中的类型参数外,不能使用未知的类型。功能定义的类比应该有助于消除混淆。