0

我使用 Dart2JS 编译器版本 1.0.0_r30798 (STABLE)。

示例代码(仅用于引入问题):

此处的真实代码(现已针对 dart2js 行为进行了更正):https ://github.com/mezoni/queries/blob/master/lib/src/queries/lookup.dart

这是 Dart 语言的 Queryable 集合的一部分。

class ILookup<TKey, TElement> implements IEnumerable<IGrouping<TKey, TElement>> {
}

class Lookup<TKey, TElement> extends Object with Enumerable implements ILookup<TKey, TElement> {
}

class IEnumerable<T> implements HasIterator<T> {
}

class HasIterator<T> {
}

class IGrouping<TKey, TElement> implements IEnumerable<TKey> {
}

class Enumerable<T> implements IEnumerable<T> {
}

void main() {
  var obj = new Lookup();
  print(obj);
}

此代码生成 Google Dart dart2js 编译器的以下错误:

 Internal Error: Inheritance of the same class with different type arguments is not
 supported: Both HasIterator<dynamic> and HasIterator<IGrouping<TKey, TElement>> are
 supertypes of Lookup<TKey, TElement>.
 class Lookup<TKey, TElement> extends Object with Enumerable implements ILookup<TKey,
 TElement> {

 ^^^^^
 Error: Compilation failed.

也就是说,dart2js编译器无法编译这段代码。

所以,我无法理解:“这是错误、功能还是限制?”。

4

2 回答 2

3

首先,dart2js 不是适用于语言规范的 Dart VM 编译器。因为 Dart VM 和 Javascript 是不同的语言,在非常抽象的极端情况下可能会发生不同的行为或限制。他们不应该,但他们会。

话虽如此,我不明白为什么这段代码首先能够在虚拟机中运行。根据我对 Dart 中继承和 mixins 的了解,您的定义Lookup应如下所示:

class Lookup<TKey, TElement> extends Object with Enumerable<IGrouping<TKey, TElement>> implements ILookup<TKey, TElement>`

因为否则,正如错误消息所说,您将使用不同的类型参数继承 HasIterator 两次,这当然有点问题 - 如果您最终将方法添加到 HasIterator,应该调用这两个中的哪一个?method1(dynamic)还是method1(IGrouping<TKey, TElement>)

于 2014-01-12T17:20:55.900 回答
1

Dart Team 的回答非常好。

“VM 行为是正确的,dart2js 还没有实现它。”

https://code.google.com/p/dart/issues/detail?id=16047#c5

也从 Gilad Bracha 回答。

“FWIW,规范没有这样的限制”(RE:类实现具有两个不同类型参数的接口)。

https://code.google.com/p/dart/issues/detail?id=14729#c2

也很好地提到:

“不幸的是,目前这是 dart2js 中的一个故意限制。使用不同类型的参数实现相同的接口从未真正奏效,因此让人们依赖于被破坏的行为让我们感到非常不舒服。”

https://code.google.com/p/dart/issues/detail?id=14729#c3

这个答案完全符合原始问题中的示例代码正确,并且目前无法通过 dart2js 编译。

附言

我的想法(我的跳线):

我认为这个问题可以在 Dart2JS 编译器中通过更好地测试类型兼容性来解决,而不仅仅是通过测试类的相等性。

我认为在这种情况下HasIterator<dynamic>,并且不是相同的类型(即使它们是相同的类),因为它们都只是隐式指定参数的HasIterator<IGrouping<TKey, TElement>>下限和上限。TElementHasIterator<TElement>

实际上,这更复杂,我可以在这里解释,但我可以添加以下内容。

它们的类型不同,因为这个表达式为真:

HasIterator<dynamic> != HasIterator<IGrouping<TKey, TElement>>

它们不冲突(但隐式指定下限和上限),因为以下表达式中的一个为真。

HasIterator<dynamic> is HasIterator<IGrouping<TKey, TElement>>
HasIterator<IGrouping<TKey, TElement>> is HasIterator<dynamic>

我们的情况是(隐式)下限是dynamic,(隐式)上限bound<IGrouping<TKey, TElement>

implicit术语仅表示resolved at compile time.

这意味着它们中的一个是另一个的子类型,编译器必须在声明中允许它们两者。并且在类型注释中,编译器必须测试参数与它们两者的兼容性(包括其他超级接口)。

如果 Dart2JS 将更彻底地测试超类型,它可以绕过这个问题。

我不想在这里给出示例,但我认为开发人员知道如何解决这个问题。

于 2014-01-13T10:06:45.683 回答