首页 > 解决方案 > 实现抽象类的类的类型与抽象类的泛型类型不兼容

问题描述

我试图为数据库建模。

我有三个用于数据库、模式和表的抽象类。每个数据库应该能够返回所有模式,每个模式应该能够返回所有表。接下来,我为一个数据库创建了实例,该数据库具有两个模式,每个模式都包含表。

抽象类具有类型参数以确保不同数据库的模式类型不混合。所有数据库的模式都应该是模式,但特定数据库的特定模式不应该在另一个数据库中使用,而是在它自己的数据库中使用。

这是代码:

import java.util.List;

public class main
{
  // Abstract

  static abstract class Database
  {
    public abstract List<Schema<? extends Database>> schemas ();
  }

  static abstract class Schema<D extends Database>
  {
    public abstract List<Table<D,? extends Schema<D>>> tables ();
  }

  public abstract class Table<D extends Database,S extends Schema<D>>
  {
  }

  // Concrete

  static class Database_1 extends Database
  {
    @Override
    public List<Schema<Database_1>> schemas () {
      return List.of (new Database_1_Schema_1(),
                      new Database_1_Schema_2());
    }
  }

  static class Database_1_Schema_1 extends Schema<Database_1>
  {
    @Override
    public List<Table<Database_1, Database_1_Schema_1>> tables () {
      return List.of (new Database_1_Schema_1_Table_1(),
                      new Database_1_Schema_1_Table_2());
    }
  }

  static class Database_1_Schema_1_Table_1 extends Table<Database_1, Database_1_Schema_1> { }
  static class Database_1_Schema_1_Table_2 extends Table<Database_1, Database_1_Schema_1> { }

  static class Database_1_Schema_2 extends Schema<Database_1>
  {
    @Override
    public List<Table<Database_1, Database_1_Schema_2>> tables () {
      return List.of (new Database_1_Schema_2_Table_1(),
                      new Database_1_Schema_2_Table_2());
    }
  }

  static class Database_1_Schema_2_Table_1 extends Table<Database_1, Database_1_Schema_2> { }
  static class Database_1_Schema_2_Table_2 extends Table<Database_1, Database_1_Schema_2> { }

  // Main

  public static void main (String ...arguments) throws Exception
  {
  }
}

代码无法编译。当我尝试编译它时,我得到了几个错误。其中大多数是后果性错误。我不明白的主要错误如下:

main.java:26: error: schemas() in Database_1 cannot override schemas() in Database
    public List<Schema<Database_1>> schemas () {
                                    ^
  return type List<Schema<Database_1>> is not compatible with List<Schema<? extends Database>>

类型

不兼容:

为什么?外观非常相似。第一个是第二个的子集。为什么不能用具体类的具体类型来满足抽象类的一般要求呢?

标签: java

解决方案


Cat成为 的子类型Animal。然后我们写猫≺动物。让PersianCat是 的子类型Cat,因此 PersianCat ≺ Cat。

关系是传递的。波斯猫 ≺ 猫 && 猫 ≺ 动物 => 波斯猫 ≺ 动物。该关系是自反的(Cat ≺ Cat),但不是对称的(Cat ≺ Animal 并不意味着 Animal ≺ Cat)。因此,关系是一个顺序

不变性

然而,泛型类型是不变的。List<Cat>根本不是的子类型List<Animal>,并且有充分的理由考虑以下代码片段:

List<Animal> animals = new List<Animal>();
animals.add(new Dog());
animals.add(new Cat());

让我们暂时假设List<Cat>List<Animal>并重新考虑代码片段,稍作改动:

List<Animal> animals = new List<Cat>(); // valid, since List<Cat> ≺ List<Animal>
animals.add(new Cat());
animals.add(new Dog()); // bäng! But I am allowed to put Dogs into List<Animal>, no?

因此,泛型是不变的。它们都保证您可以投入其中以及从中获得什么。您可以通过使用 Co-or Contarvariant 类型边界来缓解这些要求。

逆变

List<? super Dog>逆变的。我们只能将 Dogs 放入其中,但对于我们取出的内容却一无所知。因此,我们将 Object 取出,因为它是类型层次结构的根(它在我们的子类型关系中最大的元素,顺序的上级)。

List<? super Dog> dogs = new ArrayList<Animal>();这行得通,因为我们可以将 Dog 放入其中。而且动物是物体,所以我们可以把物体拿出来。

List<? super Dog> dogs = new ArrayList<Animal>();
// dogs.add(new Animal()); // compile error, need to put Dog in
dogs.add(new Dog());
Object obj = dogs.get(0);
// Dog dog = dogs.get(0); // compile error, can only take Object out

协方差

我们可以使用使类型协变extends。协变类型保证了你可以类型中得到什么。

List<? extends Animal>协变的。你一定会得到一只动物

List<? extends Animal> animals = new ArrayList<Cat>();是允许的,因为猫是动物,(猫≺动物)并get(int)给你动物。诚然,它们都是猫,但猫是动物,所以这很好。

但是,添加东西更难,因为您实际上没有可以输入的类型:

List<? extends Animal> animals = new ArrayList<Cat>();
//animals.add(new Cat()); // compile error
//animals.add(new Animal()); // compile error
Animal animal = animals.get(0);

List<? extends Cat> cats = new ArrayList<Animal>();编译器错误,因为您可以取出任何动物 - 但您要求唯一可以取出的是猫。

您的代码和错误

return type List<Schema<Database_1>> is not compatible with List<Schema<? extends Database>>

Schema<Database_1> 不是 的子类型Schema<? extends Database>Schema<? extends Database>必须接受任何持有 T ≺ 数据库的类型 T。但是,Schema<Database_1> 接受持有 T ≺ Database_1 的类型。

考虑以下代码片段:

Schema<? extends Database> schema = new Schema<>(new Database_2()); // perfectly valid
List<Schema<? extends Database>> schemas_N = new ArrayList<>();
schemas_N.add(schema); // perfectly valid

List<Schema<Database_1>> schemas_1 = new ArrayList<>();
schemas_1.add(schema); // error, as expected!

// but this is also expected
schemas_N = schemas_1; // error

最后一行现在应该很清楚了。我们不能分配schemas_Nlist schemas_1,因为两者都期望不同的类型。它们根本不兼容。

因此,如果您的方法预期返回 a List<Schema<? extends Database>>(这意味着您可以将 aSchema<Database_2>放入其中),则不能返回 a List<Schema<Database_1>>,因为它不接受上述类型。覆盖方法时,您不能以不兼容的方式更改它们,因此您会看到以下错误:

return type List<Schema<Database_1>> is not compatible with List<Schema<? extends Database>>

推荐阅读