通用类型参数和继承的问题

An issue with Generic type parameters and Inheritance

无法具体化标题,抱歉。

比如我们有一个Parentclass和一个Childclass:

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.

所以,我的问题是,为什么编译器看不到 AParent 的 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 parametersA extends Something 一样 不可 可写(以及 upper-bounded 通配符 ? extends Something).除了 类型 Anull 其他变量 之外,任何尝试将任何东西分配给 变量 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 将被实例化。并且编译不掌握是否ChildGrandChild等信息,因此不会认为这些操作是安全的。

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-boundedA 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