将泛型类型添加到 Dart 中的可比较类型

Adding the generic type to a comparable type in Dart

这是阅读此问答后的后续问题:

我有一个 class 像这样:

class BinarySearchTree<E extends Comparable> { ... }

所以我可以像这样创建一个实例:

final tree = BinarySearchTree<int>();

我的问题是关于使用 ComparableComparable<E>。当我这样做时:

class BinarySearchTree<E extends Comparable> { ... }

则类型默认为E extends Comparable<dynamic>。我通常尽量避免 dynamic,所以为了更明确地说明正在比较的类型,我似乎应该这样写:

class BinarySearchTree<E extends Comparable<E>> { ... }

但在那种情况下,我在这里得到一个错误:

final tree = BinarySearchTree<int>();
// 'int' doesn't conform to the bound 'Comparable<int>' of the type parameter 'E'.
// Try using a type that is or is a subclass of 'Comparable<int>'.

这表明我对泛型缺乏理解。我错过了什么?

在 Dart 中,class 不能实现通用接口的 2 个不同的具体实例:

abstract class Foo<T> {}

// error: Foo can only be implemented once
class Bar implements Foo<String>, Foo<int> {}

num 实现了 Comparable<num>,因为内置数字类型不可比较有点荒谬。但是,由于 intnum 的子类型(因此继承了 Comparable<num>,它不能有 Comparable<int>.

这导致 int 没有实现 Comparable<int>.

的稍微奇怪的结果

你面临的问题是,从语言的角度来看,涉及两种类型:被比较的元素的类型,以及它们被比较的元素的类型。

因此,您的类型将需要 2 个类型参数:

class Tree<T extends Comparable<S>, S> {
  T get foo;
}

final intTree = Tree<int, num>();
final foo = intTree.foo;  // returns an int

诚然,这不是一个超级干净的解决方案,但如果您使用的是 Dart 2.13 或更高版本,则可以使用 typedef 使其更好一些:

typedef IntTree = Tree<int, num>;
typedef RegularTree<T> = Tree<T, T>;

final intTree = IntTree();
final stringTree = RegularTree<String>();

intTree.foo  // is an int
stringTree.foo  // is a String

还有另一种选择,就是放弃一些类型安全并使用 Comparable<dynamic>,但我个人不建议这样做。顺便说一句,如果你想避免意外丢失类型参数,你可以禁用 implicit-dynamic ,如下所述:https://dart.dev/guides/language/analysis-options#enabling-additional-type-checks

每当从上下文中推断类型 dynamic 而程序员实际上没有输入单词 dynamic

时,这将给出错误