java - 实现抽象类的类的类型与抽象类的泛型类型不兼容
问题描述
我试图为数据库建模。
我有三个用于数据库、模式和表的抽象类。每个数据库应该能够返回所有模式,每个模式应该能够返回所有表。接下来,我为一个数据库创建了实例,该数据库具有两个模式,每个模式都包含表。
抽象类具有类型参数以确保不同数据库的模式类型不混合。所有数据库的模式都应该是模式,但特定数据库的特定模式不应该在另一个数据库中使用,而是在它自己的数据库中使用。
这是代码:
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>>
类型
List<Schema<Database_1>>
与Database_1
扩展Database
不兼容:
List<Schema<? extends Database>>
.
为什么?外观非常相似。第一个是第二个的子集。为什么不能用具体类的具体类型来满足抽象类的一般要求呢?
解决方案
让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_N
list 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>>
推荐阅读
- javascript - 如何在 React useEffect 中使用函数
- python-3.x - 来自客户端的 Djanog 应用程序快速书授权页面
- python - 如何在 Python 中创建设计矩阵?
- entity-framework-core - 实体框架:具有大量数据的缓慢迁移
- heroku - Heroku 应用程序已部署但无法运行,并且出现错误
- react-native - 如何在 React Native 中重新播放 Lottie 动画
- php - MySQL 字段看似随机更新
- javascript - 在 Tabulator 4.9 中使用 setColumns 函数时缺少格式化程序
- laravel - 让 Foundation 使用 Laravel - 没有 SCSS 文件夹
- android - 高端 iOS 和 Android 手机的 BLE 扫描时间