通用类型在Dart中被继承覆盖。

huangapple go评论60阅读模式
英文:

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,其中 ESomeThing 类:

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&lt;E&gt; {
  List&lt;E&gt; 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&lt;SomeThing&gt;(list: List&lt;SomeThing&gt;.empty());

At this point, I assumed that the type of buck.list is List&lt;SomeThing&gt;. Then, I assign it with List&lt;SomethingOther&gt;.

buck.list = List&lt;SomethingOther&gt;.empty();

At this point, there is nothing unusual. I assume that type of buck.list is still List&lt;SomeThing&gt;.
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&lt;SomeThing&gt;(list: List&lt;SomeThing&gt;.empty());
  buck.list = List&lt;SomethingOther&gt;.empty();
  buck.list.add(SomeThing());
}

class Buck&lt;E&gt; {
  List&lt;E&gt; 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&lt;Derived&gt; to be a subtype of Generic&lt;Base&gt;. Consequently, a Generic&lt;Derived&gt; is assignable to (or returnable as) a Generic&lt;Base&gt;.

对于大多数泛型情况,这通常不是问题,它通常提供了便利性。对于集合而言,这可能导致你遇到的问题。List&lt;Derived&gt; 只能存储 Derived 元素。因此,将 List&lt;Derived&gt; 分配给 List&lt;Base&gt; 实际上是不安全的,如果后续代码尝试添加其他 Base 元素。

这很不幸。

为了避免这种情况,你应该确保构建一个 List&lt;Base&gt;(在你的示例中是 List&lt;Something&gt;),即使它初始化为 Derived(在你的情况下是 SomethingOther)元素。例如:

var list = &lt;Something&gt;[SomethingOther(), SomethingOther()];
var buck = Buck(list: list);

为了更好地保护,你可以让 Buck 创建其自己的内部副本,这样它可以确保它始终具有一个 List&lt;Something&gt; 而不是 List&lt;SomethingOther&gt;

class Buck&lt;E&gt; {
  List&lt;E&gt; _list = &lt;E&gt;[];

  List&lt;E&gt; get list =&gt; _list;

  set list(List&lt;E&gt; newList) {
    _list.clear();
    _list.addAll(newList);
  }

  Buck({required List&lt;E&gt; list}) {
    this.list = list;
  }
}

还请参阅:

英文:

If Derived is a subtype of Base, then Dart also treats Generic&lt;Derived&gt; to be a subtype of Generic&lt;Base&gt;. Consequently, a Generic&lt;Derived&gt; is assignable to (or returnable as) a Generic&lt;Base&gt;.

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&lt;Derived&gt; can store only Derived elements. Therefore assigning a List&lt;Derived&gt; to a List&lt;Base&gt; 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&lt;Base&gt; (which in your example, is List&lt;Something&gt;), even if it's initialized with Derived (SomethingOther in your case) elements. For example:

var list = &lt;Something&gt;[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&lt;Something&gt; and not a List&lt;SomethingOther&gt;:

class Buck&lt;E&gt; {
  List&lt;E&gt; _list = &lt;E&gt;[];

  List&lt;E&gt; get list =&gt; _list;

  set list(List&lt;E&gt; newList) {
    _list.clear();
    _list.addAll(newList);
  }

  Buck({required List&lt;E&gt; list}) {
    this.list = list;
  }
}

Also see:

huangapple
  • 本文由 发表于 2023年5月11日 06:22:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76222940.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定