通用类型参数和继承的问题
An issue with Generic type parameters and Inheritance
无法具体化标题,抱歉。
比如我们有一个Parent
class和一个Child
class:
package test;
public class Parent {
}
package test;
public class Child extends Parent {
}
还有一个 class 可以在其名为 a
的字段中包含 Parent
的 child:
package test;
public class Holder<A extends Parent> {
public A a;
}
让我们创建一个 class 的实例并尝试为 a
赋值:
package test;
public class Main {
public static void main(String[] main) {
Holder<Child> holder = new Holder<>();
holder.a = new Child();
}
}
到目前为止,还不错。但是假设我们想将此赋值移动到 Holder
的构造函数:
package test;
public class Holder<A extends Parent> {
public A a;
public Holder() {
a = new Child();
}
}
现在我们收到一个错误:
Error:(7, 13) java: incompatible types: test.Child cannot be converted to A
.
所以,我的问题是,为什么编译器看不到 A
是 Parent
的 child?即使我们设置<A extends Parent>
。为什么我们可以在 class 之外赋值,但不能在其中赋值?我能以某种方式解决这个问题吗?
我知道我们可以用 Parent
替换 A
并删除该类型参数或将 new Child()
传递给 Main
中的构造函数参数,但这是因为我给问题的简化示例。
A
是一个 [type] 参数。它不是实际类型。与大多数 类 一样,您可以通过方法构造函数中的参数初始化成员。
public class Main {
public static void main(String[] args) {
new Holder<Child>(new Child());
}
}
class Parent {
}
class Child extends Parent {
}
class Holder<A extends Parent> {
public A a;
public Holder(A a) {
this.a = a;
}
}
Bounded generic type parameters 与 A extends Something
一样 不可 可写(以及 upper-bounded 通配符 ? extends Something
).除了 类型 A
和 null
的 其他变量 之外,任何尝试将任何东西分配给 变量 a
会失败.
为了理解原因,让我们考虑以下代码:
public class Parent {}
public class Child extends Parent {}
public class GrandChild extends Child {}
public class Holder<A extends Parent> {
public A a;
public void setA(A a) {
this.a = a;
}
}
Holder
class 中的 A
只是将在 运行时 提供的类型的 place-holder。这是一种告诉编译器我们现在不知道类型是什么的方法。但它将是一个在其继承链中具有 Parent
class 的特定类型。编译器将在检查对变量 a
执行的操作是否安全时考虑该信息。
在下面的代码中,编译器将不允许将 Child
的对象分配为 a
的值,因为它与类型 GrandChild
不兼容(恰好是A
的实际类型)。只有 GrandChild
及其子类型的实例可以毫无问题地分配。
public static void main(String[] args) {
Holder<GrandChild> grandChildHolder = new Holder<>();
grandChildHolder.setA(new GrandChild()); // no issues
grandChildHolder.setA(new Child()); // compilation error
}
类似地,Holder
class 中的以下赋值不会成功,因为类型 A
将仅被知道 Holder
class 将被实例化。并且编译不掌握是否Child
、GrandChild
等信息,因此不会认为这些操作是安全的。
public class Holder<A extends Parent> {
public A a;
// instance initialither block (runs when Holder object is being created)
{
a = new Child(); // compilation error - type A could be a GrandChild
a = new GrandChild(); // compilation error - type A could potentially be represented by class incompatible with GrandChild
a = null; // no issues because null is a valid value for any type
}
public void setA(A a) {
this.a = a;
}
}
Upper-bounded 像 A extends Parent
这样的通用参数在想要使 class 或方法能够与不同的类型同时对有效类型的范围施加一定的限制,以便访问 Parent
class 的行为(让我们假设有一些像 work()
, goShoping()
, 等).
如果您在没有 extends 子句 的情况下声明持有人,则仅 Holder<A>
Object
class 的方法(hashCode()
, equals()
, toString()
) 将可以通过 A
.
类型的变量和参数访问
此外,子句 extends Parent
将允许编译器发现引入无效类型参数的尝试:
Holder<String> stringHolder; // error:
// type argument String is not within the bounds of type A
无法具体化标题,抱歉。
比如我们有一个Parent
class和一个Child
class:
package test;
public class Parent {
}
package test;
public class Child extends Parent {
}
还有一个 class 可以在其名为 a
的字段中包含 Parent
的 child:
package test;
public class Holder<A extends Parent> {
public A a;
}
让我们创建一个 class 的实例并尝试为 a
赋值:
package test;
public class Main {
public static void main(String[] main) {
Holder<Child> holder = new Holder<>();
holder.a = new Child();
}
}
到目前为止,还不错。但是假设我们想将此赋值移动到 Holder
的构造函数:
package test;
public class Holder<A extends Parent> {
public A a;
public Holder() {
a = new Child();
}
}
现在我们收到一个错误:
Error:(7, 13) java: incompatible types: test.Child cannot be converted to A
.
所以,我的问题是,为什么编译器看不到 A
是 Parent
的 child?即使我们设置<A extends Parent>
。为什么我们可以在 class 之外赋值,但不能在其中赋值?我能以某种方式解决这个问题吗?
我知道我们可以用 Parent
替换 A
并删除该类型参数或将 new Child()
传递给 Main
中的构造函数参数,但这是因为我给问题的简化示例。
A
是一个 [type] 参数。它不是实际类型。与大多数 类 一样,您可以通过方法构造函数中的参数初始化成员。
public class Main {
public static void main(String[] args) {
new Holder<Child>(new Child());
}
}
class Parent {
}
class Child extends Parent {
}
class Holder<A extends Parent> {
public A a;
public Holder(A a) {
this.a = a;
}
}
Bounded generic type parameters 与 A extends Something
一样 不可 可写(以及 upper-bounded 通配符 ? extends Something
).除了 类型 A
和 null
的 其他变量 之外,任何尝试将任何东西分配给 变量 a
会失败.
为了理解原因,让我们考虑以下代码:
public class Parent {}
public class Child extends Parent {}
public class GrandChild extends Child {}
public class Holder<A extends Parent> {
public A a;
public void setA(A a) {
this.a = a;
}
}
Holder
class 中的 A
只是将在 运行时 提供的类型的 place-holder。这是一种告诉编译器我们现在不知道类型是什么的方法。但它将是一个在其继承链中具有 Parent
class 的特定类型。编译器将在检查对变量 a
执行的操作是否安全时考虑该信息。
在下面的代码中,编译器将不允许将 Child
的对象分配为 a
的值,因为它与类型 GrandChild
不兼容(恰好是A
的实际类型)。只有 GrandChild
及其子类型的实例可以毫无问题地分配。
public static void main(String[] args) {
Holder<GrandChild> grandChildHolder = new Holder<>();
grandChildHolder.setA(new GrandChild()); // no issues
grandChildHolder.setA(new Child()); // compilation error
}
类似地,Holder
class 中的以下赋值不会成功,因为类型 A
将仅被知道 Holder
class 将被实例化。并且编译不掌握是否Child
、GrandChild
等信息,因此不会认为这些操作是安全的。
public class Holder<A extends Parent> {
public A a;
// instance initialither block (runs when Holder object is being created)
{
a = new Child(); // compilation error - type A could be a GrandChild
a = new GrandChild(); // compilation error - type A could potentially be represented by class incompatible with GrandChild
a = null; // no issues because null is a valid value for any type
}
public void setA(A a) {
this.a = a;
}
}
Upper-bounded 像 A extends Parent
这样的通用参数在想要使 class 或方法能够与不同的类型同时对有效类型的范围施加一定的限制,以便访问 Parent
class 的行为(让我们假设有一些像 work()
, goShoping()
, 等).
如果您在没有 extends 子句 的情况下声明持有人,则仅 Holder<A>
Object
class 的方法(hashCode()
, equals()
, toString()
) 将可以通过 A
.
此外,子句 extends Parent
将允许编译器发现引入无效类型参数的尝试:
Holder<String> stringHolder; // error:
// type argument String is not within the bounds of type A