为什么当我在swift中扩展ExpressibleByArrayLiteral协议时,我需要使init成为必需的。在协议的定义中,init方法只是公开的。
我几乎和文档https://developer.apple.com/reference/swift/expressiblebyarrayliteral中的内容相同,但编译器仍然抱怨要求init(arrayLiteral: Element...)
。我唯一的区别是我在一个没有结构的类中实现它。有什么建议吗?
更新
以下是我的代码的实现:
public class Stack<T> {
private var buffer: [T]
init() {
self.buffer = []
}
public func push(_ value: T) {
self.buffer.append(value)
}
public func pop() -> T? {
return self.buffer.popLast()
}
var size: Int {
return self.buffer.count
}
var isEmpty: Bool {
return self.size == 0
}
}
extension Stack: ExpressibleByArrayLiteral {
init(arrayLiteral: T...) {
for item in arrayLiteral {
self.push(item)
}
}
}
我得到的错误是:
1)指定的初始值设定项不能在&#39; Stack&#39 ;;的扩展名中声明。你是说这是一个方便的初始化器吗?
2)初始化程序&#init;(arrayLiteral:)&#39;必须公开,因为它符合公共协议中的要求&#39; ExpressibleByArrayLiteral&#39;
3)初始化程序要求&quot; init(arrayLiteral :)&#39;只能通过非最终类定义中的required
初始化程序来满足&#39; Stack&#39;
问题:
错误#1 - &gt; 为什么我无法在扩展程序中声明指定的init?
对于错误#2 - &gt; 我理解这一部分,协议将此定义为公开。但是为什么文档用public关键字实现呢?
错误#3 - &gt; 这是我最大的问题,为什么需要这样做。
感谢您的帮助!
答案 0 :(得分:4)
原因是继承:如果类被采用,协议中的任何frame.getRootPane().getInputMap(...)...
frame.getRootPane().getActionMap()...
都必须标记为init
。 <{1}}无法继承。
对于某个类,An required
必须初始化该同一个类的实例或返回struct
。当该类采用协议时,其本身及其所有子类必须提供自己的实现,以便编译器可以验证这一事实:
init
这是由于Swift的静态打字系统。与您认为创建nil
但实际上获得class ClassA : ExpressibleByArrayLiteral {
required init(arrayLiteral elements: Self.Element...) {
// this implicitly returns an instance of ClassA
}
}
class ClassB : ClassA {
// without its own init(arrayLitteral:), it will fallback on ClassA's init and return
// an instance of ClassA, which Swift does not allow.
}
的Objective-C不同(所谓的class cluster设计模式)
NSString
摆脱扩展并在类的定义中实现它:
_NSContiguousString
答案 1 :(得分:2)
错误#1 - &gt;为什么我不能在扩展名中声明指定的init?
因为便利初始化器必须调用指定的初始化器。扩展可以在其他文件中定义。使用当前结构,类可能没有在此编译单元(文件)中定义的指定初始化程序,这对当前编译器造成了太多混淆。 (并不是说这是一个无法解决的类型理论问题;它只是在需要弄清楚单个文件中的所有内容时才会产生问题。)扩展是扩展。如果它们从根本上改变了对象的工作方式(比如创建指定的初始化器),那么它们可能无法成为扩展。
错误#2 - &gt;我理解这一部分,协议将此定义为公开。但是为什么文档用public关键字实现呢?
因为doc渲染器总是剥离协议上的public
。如果这引起混淆,请向bugs.swift.org打开一个错误。
错误#3 - &gt;这几乎是我最大的问题,为什么需要这样做。
因为这是一个非最终类。考虑是否有Stack
的子类具有自己的必需初始值设定项。你如何确保init(arrayLiteral:)
调用它? 无法调用它(因为它不知道它存在)。因此,init(arrayLiteral:)
必须是required
(这意味着它必须是主要声明的一部分,而不是扩展名),或Stack
必须是final
。
如果你标记final
,这就像你期望的那样。如果你想要它被子类化,只需将它移出扩展并进入主体,然后像这样定义它:
public convenience required init(arrayLiteral: T...) {
self.init()
for item in arrayLiteral {
self.push(item)
}
}