英文:
Generic type is overridden by inheritance in dart
问题
我创建了一个包含通用数据类型的类。
class Buck<E> {
List<E> list;
Buck({
required this.list,
});
}
然后,我创建了一个类和一个子类:
class SomeThing {}
class SomethingOther extends SomeThing {}
我创建了一个对象 Buck
,其中 E
是 SomeThing
类:
var buck = Buck<SomeThing>(list: List<SomeThing>.empty());
此时,我假设 buck.list
的类型是 List<SomeThing>
。然后,我将其赋值为 List<SomethingOther>
:
buck.list = List<SomethingOther>.empty();
此时,没有什么异常情况。我假设 buck.list
的类型仍然是 List<SomeThing>
。
然而,当我尝试将 SomeThing
对象添加到 buck.list
时,我得到了一个错误:
buck.list.add(SomeThing()); // 这会导致错误
以下是完整的代码:
void main() {
var buck = Buck<SomeThing>(list: List<SomeThing>.empty());
buck.list = List<SomethingOther>.empty();
buck.list.add(SomeThing());
}
class Buck<E> {
List<E> list;
Buck({
required this.list,
});
}
class SomeThing {}
class SomethingOther extends SomeThing {}
为什么会发生这种情况?这是预期的行为吗?
英文:
I created a class that contains a generic data type.
class Buck<E> {
List<E> list;
Buck({
required this.list,
});
}
Then, I created a class and a subclass:
class SomeThing {}
class SomethingOther extends SomeThing {}
I created an object Buck
with E
is SomeThing
class.
var buck = Buck<SomeThing>(list: List<SomeThing>.empty());
At this point, I assumed that the type of buck.list
is List<SomeThing>
. Then, I assign it with List<SomethingOther>
.
buck.list = List<SomethingOther>.empty();
At this point, there is nothing unusual. I assume that type of buck.list
is still List<SomeThing>
.
However, when I try to add SomeThing
object to buck.list
, I get an error:
buck.list.add(SomeThing()); // this will be error
Here is the complete code:
void main() {
var buck = Buck<SomeThing>(list: List<SomeThing>.empty());
buck.list = List<SomethingOther>.empty();
buck.list.add(SomeThing());
}
class Buck<E> {
List<E> list;
Buck({
required this.list,
});
}
class SomeThing {}
class SomethingOther extends SomeThing {}
Why does this happen? Is this expected behavior?
答案1
得分: 2
If Derived
is a subtype of Base
, then Dart also treats Generic<Derived>
to be a subtype of Generic<Base>
. Consequently, a Generic<Derived>
is assignable to (or returnable as) a Generic<Base>
.
对于大多数泛型情况,这通常不是问题,它通常提供了便利性。对于集合而言,这可能导致你遇到的问题。List<Derived>
只能存储 Derived
元素。因此,将 List<Derived>
分配给 List<Base>
实际上是不安全的,如果后续代码尝试添加其他 Base
元素。
这很不幸。
为了避免这种情况,你应该确保构建一个 List<Base>
(在你的示例中是 List<Something>
),即使它初始化为 Derived
(在你的情况下是 SomethingOther
)元素。例如:
var list = <Something>[SomethingOther(), SomethingOther()];
var buck = Buck(list: list);
为了更好地保护,你可以让 Buck
创建其自己的内部副本,这样它可以确保它始终具有一个 List<Something>
而不是 List<SomethingOther>
:
class Buck<E> {
List<E> _list = <E>[];
List<E> get list => _list;
set list(List<E> newList) {
_list.clear();
_list.addAll(newList);
}
Buck({required List<E> list}) {
this.list = list;
}
}
还请参阅:
英文:
If Derived
is a subtype of Base
, then Dart also treats Generic<Derived>
to be a subtype of Generic<Base>
. Consequently, a Generic<Derived>
is assignable to (or returnable as) a Generic<Base>
.
For most generics, that isn't a problem, and it usually provides convenience. For collections, it can lead to the problem you encountered. A List<Derived>
can store only Derived
elements. Therefore assigning a List<Derived>
to a List<Base>
is not actually safe if code later tries to add other Base
elements.
It's unfortunate.
To avoid this, you should ensure that you construct a List<Base>
(which in your example, is List<Something>
), even if it's initialized with Derived
(SomethingOther
in your case) elements. For example:
var list = <Something>[SomethingOther(), SomethingOther()];
var buck = Buck(list: list);
For better protection, you can make Buck
create its own internal copy of the input List
, and that way it can ensure that it always has exactly a List<Something>
and not a List<SomethingOther>
:
class Buck<E> {
List<E> _list = <E>[];
List<E> get list => _list;
set list(List<E> newList) {
_list.clear();
_list.addAll(newList);
}
Buck({required List<E> list}) {
this.list = list;
}
}
Also see:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论