我使用的是Dart2JS编译器版本1.0.0_r30798(STABLE)。
示例代码(仅用于介绍问题):
此处的实际代码(现已针对dart2js行为进行了更正):https://github.com/mezoni/queries/blob/master/lib/src/queries/lookup.dart
这是其中的一部分 Queryable collections for Dart language
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
编译器无法编译此代码。
所以,我无法理解:“这个错误,功能还是限制?”。
答案 0 :(得分:3)
首先,dart2js不是适用于语言规范的Dart VM编译器。由于Dart VM和Javascript是不同的语言,因此可能会出现非常抽象的极端情况中的不同行为或限制。他们不应该,但他们确实如此。
话虽如此,我不明白为什么这个代码首先能够在VM中运行。根据我对Dart中的继承和mixin的了解,您对Lookup
的定义应如下所示:
class Lookup<TKey, TElement> extends Object with Enumerable<IGrouping<TKey, TElement>> implements ILookup<TKey, TElement>`
因为否则,正如错误消息所示,您将使用不同的类型参数继承HasIterator两次,这当然有点问题 - 如果您最后向HasIterator添加方法,那么应该调用哪两个? method1(dynamic)
或method1(IGrouping<TKey, TElement>)
?
答案 1 :(得分:1)
Dart Team的答案非常好。
“VM行为正确,dart2js尚未实现它。”
https://code.google.com/p/dart/issues/detail?id=16047#c5
Gilad Bracha也回答。
“FWIW,规范没有这样的限制”(RE:class实现了一个带有两个不同类型参数的接口)。
https://code.google.com/p/dart/issues/detail?id=14729#c2
也很好地提到:
“不幸的是,这是dart2js中的故意限制。使用不同类型参数实现相同的接口从未真正起作用,因此我们感到非常不舒服让人们依赖于破坏的行为。”
https://code.google.com/p/dart/issues/detail?id=14729#c3
这个答案完全符合原始问题中的示例代码是正确的,目前无法通过dart2js进行编译。
P.S。
我的想法(我的跳投):
我认为这个问题可以通过更好的类型兼容性测试在Dart2JS编译器中解决,但不仅仅是通过测试类的相等性。
我认为在这种情况下HasIterator<dynamic>
和HasIterator<IGrouping<TKey, TElement>>
不是相同的类型(即使它们是相同的类),因为它们都隐式指定了TElement
参数的下限和上限。 HasIterator<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会更彻底地测试超类型,它可以绕过这个问题。
我不想在这里举例说明这是可能的,但我认为开发人员知道如何解决这个问题。