满足ExpressibleByArrayLiteral协议

时间:2017-03-08 15:53:53

标签: swift protocols

为什么当我在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; 这是我最大的问题,为什么需要这样做。

感谢您的帮助!

2 个答案:

答案 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)
    }
}