本文介绍了如何编写抽象类构造函数,以便它可以灵活扩展子类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 我试图实现一个持久的 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。 这些限制似乎强制类作者考虑如何该类将被扩展。 扩展列表 class 从 dart:collection 包似乎确认这个结论 - 有一个完整的单独的类用于扩展,我不能直接扩展 List 类本身。 我的问题是更一般的,然后上述的问题:它可以灵活扩展吗?这包括允许使用以下功能: 超类中的工厂构造函数 正在构造函数 const 用作mixin 虽然我理解使用mixin可能是不可能或不需要的,但其他点仍然有效。最重要的是,问题是:为什么我不能扩展一个具有工厂构造函数的类?这与我熟悉的任何其他OO语言不同。 还有相关问题: 扩展只有一个工厂构造函数的类 在Dart中扩展异常类 编辑:感谢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 constructorI 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 classnormal constructors in sub classconst constructors in sub classbe used as a mixinWhile 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:Extending a class with only one factory constructorExtending the Exception class in DartEDIT: 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 classIf 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 classWhat was the actual question here? I guess this is the same as 1.const constructors in sub classIf 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 mixinThe restrictions for classes to be used as a mixin are temporary and should be removed at some point. 这篇关于如何编写抽象类构造函数,以便它可以灵活扩展子类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
08-18 13:33