【发布时间】:2014-12-29 05:00:16
【问题描述】:
我正在尝试实现一个持久的Stack 数据结构。我想将其实现为代数数据类型,因此它有两个具体的子类型:empty 和 non empty:
abstract class Stack<T> {
factory Stack.empty() => const _EmptyStack._();
T get data;
Stack<T> get bottom;
bool get isEmpty;
Stack<T> put(T item) => new _StackImpl(item, this);
}
class _StackImpl<T> extends Stack<T> {
final T _data;
final Stack<T> _bottom;
_StackImpl(T this._data, Stack<T> this._bottom);
T get data => _data;
Stack<T> get bottom => _bottom;
bool get isEmpty => false;
}
class _EmptyStack<T> extends Stack<T> {
const _EmptyStack._();
T get data => throw new CollectionIsEmpty();
Stack<T> get bottom => throw new CollectionIsEmpty();
bool get isEmpty => true;
}
这段代码在具体实现中引发了两个错误:
[error] The class 'Stack' does not have a default generative constructor
我找到了一个看起来像address this problem here 的示例代码,所以我通过在Stack<T> 类中放置一个无参数构造函数来修复它:
abstract class Stack<T> {
Stack();
// ...
但现在这会导致 _EmptyStack<T> 构造函数出现问题,它是常量:
Constant constructor cannot call non-constant super constructor of 'Stack<T>'
另外添加的Stack() 构造函数可防止将类用作mixin。
这些限制似乎迫使课程作者考虑如何扩展课程。来自dart:collection 包的The way of extending List class 似乎证实了这个结论——有一个完整的单独类可用于扩展,我不能直接扩展List 类本身。
我的问题比上面描述的问题更笼统:我怎样才能编写一个类,以便它可以足够灵活地扩展?这包括允许使用以下功能:
- 超类中的工厂构造函数
- 普通子类中的构造函数
-
const子类中的构造函数 - 用作混合器
虽然我知道作为 mixin 的使用可能是不可能的,甚至是不需要的,但其他点仍然有效。最重要的问题是:为什么我不能extend 一个带有工厂构造函数的类?这是一种与我熟悉的任何其他 OO 语言不同的行为。
还有相关问题:
编辑:感谢Günter Zöchbauer answer,我改进了代码,现在它可以完全运行(见下文)。我现在留下的最重要的问题是:为什么工厂构造函数破坏了扩展类的能力?以及如何解决它(除了使用基类作为接口)?一个更简单的例子来说明这一点:
class Base {
}
class _Sub extends Base {
int someValue;
_Sub(int this.someValue);
}
这段代码一切正常。但是假设我及时回到我的Base 课程并想添加工厂方法:
class Base {
factory Base.empty() => new _Sub(0);
}
现在每个扩展Base 的类都因为unresolved implicit call to super constructor 而被破坏。那我该怎么办?
原始问题的更正代码供参考:
abstract class Stack<T> {
const Stack._();
factory Stack.empty() => const _EmptyStack._();
T get data;
Stack<T> get bottom;
bool get isEmpty;
Stack<T> put(T item) => new _StackImpl(item, this);
}
class _StackImpl<T> extends Stack<T> {
final T _data;
final Stack<T> _bottom;
_StackImpl(T this._data, Stack<T> this._bottom) : super._();
T get data => _data;
Stack<T> get bottom => _bottom;
bool get isEmpty => false;
}
class _EmptyStack<T> extends Stack<T> {
const _EmptyStack._() : super._();
T get data => throw new CollectionIsEmpty();
Stack<T> get bottom => throw new CollectionIsEmpty();
bool get isEmpty => true;
}
void main(){
group('stack', (){
test('empty stack', (){
var emptyStack = new Stack.empty();
expect(emptyStack.isEmpty, isTrue);
expect(() => emptyStack.data, throwsA(new isInstanceOf<CollectionIsEmpty>()));
expect(() => emptyStack.bottom, throwsA(new isInstanceOf<CollectionIsEmpty>()));
var emptyStack2 = new Stack.empty();
expect(emptyStack == emptyStack2, isTrue);
});
test('adding to stack', (){
var stack = new Stack<String>.empty().put("a").put("b").put("c");
expect(stack.data, equals('c'));
expect(stack.bottom.data, equals('b'));
expect(stack.bottom.bottom.data, equals('a'));
});
});
}
【问题讨论】:
-
@GameAlchemist 设计是函数式编程的基本原则,它确实有效。有关该主题的更多信息,请参阅this answer。另外:正如我所说,我的问题比这个特殊情况更普遍。这只是一个例子。
标签: dart programming-languages language-design language-details