如何编写抽象类构造函数,以便它可以灵活扩展子类 [英] How to write abstract class constructors so that it will be flexible for extending in sub classes
问题描述
我试图实现一个持久的 Stack
数据结构。我想将其实现为代数数据类型,因此它有两个具体的子类型:空和非空:
抽象类Stack< T> {
factory Stack.empty()=> const _EmptyStack ._();
获取数据;
Stack< T>得到底部;
bool get isEmpty;
Stack< T> put(T item)=> new _StackImpl(item,this);
}
class _StackImpl< T>扩展Stack< T> {
final T _data;
final Stack< T> _底部;
_StackImpl(T this._data,Stack< T> this._bottom);
T get data => _数据;
Stack< T> get bottom => _底部;
bool get isEmpty =>假;
}
class _EmptyStack< T>扩展Stack< T> {
const _EmptyStack ._();
T get data => throw new CollectionIsEmpty();
Stack< T> get bottom => throw new CollectionIsEmpty();
bool get isEmpty =>真正;
}
此代码在具体实现中引发两个错误:
我发现了一个示例代码,它似乎在这里解决这个问题,所以我已经通过在 Stack< T>
类中添加一个无参数的构造函数:
抽象类Stack< T> {
Stack();
// ...
但现在这会导致 _EmptyStack< T>
构造函数,它是常量:
不能调用'Stack< T>的非常量超级构造函数
c> Stack()构造函数阻止将该类用作mixin。
这些限制似乎强制类作者考虑如何该类将被扩展。 扩展列表$ c $的方式c> class
从 dart:collection
包似乎确认这个结论 - 有一个完整的单独的类用于扩展,我不能直接扩展 List
类本身。
我的问题是更一般的,然后上述的问题:它可以灵活扩展吗?这包括允许使用以下功能:
- 超类中的工厂构造函数
- 正在构造函数
-
-
const
用作mixin
虽然我理解使用mixin可能是不可能或不需要的,但其他点仍然有效。最重要的是,问题是:为什么我不能扩展
一个具有工厂构造函数的类?这与我熟悉的任何其他OO语言不同。
还有相关问题:
编辑:感谢GünterZöchbauer答案我改进了代码,所以现在它是完全可操作的(见下文)。我现在剩下的最重要的问题是:为什么工厂构造函数打破了扩展类的能力?如何解决它(除了使用基类作为接口)?更简单的例子来说明:
class Base {
}
class _Sub extends Base {
int someValue;
_Sub(int this.someValue);
}
一切都很好。但我们假设我及时回到我的 Base
类,并希望添加工厂方法:
class Base {
factory Base.empty()=> new _Sub(0);
}
现在每个扩展 Base
被破坏,因为未解析的隐式调用超级构造函数
。我该怎么办?
修正原始问题的代码以供参考:
abstract class Stack< T> {
const Stack ._();
factory Stack.empty()=> const _EmptyStack ._();
获取数据;
Stack< T>得到底部;
bool get isEmpty;
Stack< T> put(T item)=> new _StackImpl(item,this);
}
class _StackImpl< T>扩展Stack< T> {
final T _data;
final Stack< T> _底部;
_StackImpl(T this._data,Stack< T> this._bottom):super。
T get data => _数据;
Stack< T> get bottom => _底部;
bool get isEmpty =>假;
}
class _EmptyStack< T>扩展Stack< T> {
const _EmptyStack ._():super ._();
T get data => throw new CollectionIsEmpty();
Stack< T> get bottom => throw new CollectionIsEmpty();
bool get isEmpty =>真正;
}
void main(){
group('stack',(){
test (){
var emptyStack = new Stack.empty();
expect(emptyStack.isEmpty,isTrue);
expect(()=> emptyStack.data,throwsA(new isInstanceOf< CollectionIsEmpty>()));
$ b expect()=> emptyStack.bottom,throwsA(new isInstanceOf< CollectionIsEmpty>()));
var emptyStack2 = new Stack.empty );
expect(emptyStack == emptyStack2,isTrue);
});
test('添加到stack',(){
var stack = new Stack< String> .empty()。put(a)。put(b)。put(c);
expect(stack.data, 'c'));
expect(stack.bottom.data,equals('b'));
expect(stack.bottom.bottom.data,equals('a'));
});
});
}
Stack
作为接口而不是基类。-
在超类中的工厂构造函数
如果你有一个工厂构造函数,你必须添加一个正常的构造函数,如果你想如链接问题的答案中所述扩展。 -
子类中的正则构造函数
这里的实际问题是什么?我想这与1相同。 -
在子类中的const构造函数
如果你想要一个const构造函数,所有的子类都需要一个const构造函数太。
在一个具有const构造函数的类中,所有字段都需要是final。这不是你的基类的情况,所以在_EmptyStack
中添加一个const构造函数的地方。 -
用作mixin
要用作mixin的类的限制是临时的,应该在某个时候删除。
I am trying to implement a persistent Stack
data structure. I want to implement this as an algebraic data type, so it has two concrete subtypes: empty and 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;
}
This code raises two errors in concrete implementations:
[error] The class 'Stack' does not have a default generative constructor
I found a sample code which seem to address this problem here, so I've fixed it by putting a parameterless constructor in Stack<T>
class:
abstract class Stack<T> {
Stack();
// ...
but now this causes problem with _EmptyStack<T>
constructor, which is constant:
Constant constructor cannot call non-constant super constructor of 'Stack<T>'
Additionally the added Stack()
constructor prevents from using the class as a mixin.
These restrictions seem to enforce on the class author to think about how the class would be extended. The way of extending List
class from dart:collection
package seem to confirm this conclusion - there is an entire separate class to use for extension, I can't directly extend the List
class itself.
My question is more general then the problem described above: how can I write a class so that it can be flexible enough to extend? That includes allowing the use of features like:
- factory constructors in super class
- normal constructors in sub class
const
constructors in sub class- be used as a mixin
While I understand that the use as mixin might be impossible or even unwanted, other points are still valid. Most importantly the question stands: why can't I extend
a class with a factory constructor? This is a behavior unlike any other OO language I'm familiar with.
Also related questions:
EDIT: Thanks to Günter Zöchbauer answer I've improved the code, so now it is fully operational (see below). The most important question that I am now left with is: why factory constructor breaks the ability to extend the class? And how to get around it (aside from using the base class as interface)? A simpler example to make the point:
class Base {
}
class _Sub extends Base {
int someValue;
_Sub(int this.someValue);
}
Everything is fine with this code. But let's say I get back to my Base
class in time and want to add factory method:
class Base {
factory Base.empty() => new _Sub(0);
}
Now each class which extends Base
is broken because of unresolved implicit call to super constructor
. What do I do then?
Corrected code from original question for reference:
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'));
});
});
}
In your example I suggest to just use Stack
as an interface instead of a base class.
factory constructors in super class If you have a factory constructor you have to add a normal constructor too if you want to extend as stated in the answer to the linked question.
normal constructors in sub class What was the actual question here? I guess this is the same as 1.
const constructors in sub class If you want a const constructor all subclasses need to have a const constructor too. In a class with a const constructor all fields need to be final. This isn't the case with your base class so where is the point of adding a const constructor to
_EmptyStack
.be used as a mixin The restrictions for classes to be used as a mixin are temporary and should be removed at some point.
这篇关于如何编写抽象类构造函数,以便它可以灵活扩展子类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!