【问题标题】:Swift-Generics: "Cannot specialize non-generic type"Swift-Generics:“不能专门化非泛型类型”
【发布时间】:2017-03-19 13:15:21
【问题描述】:

我尝试使用通用协议来实现面向对象的代码。 假设我有两个协议

protocol Executable: class {
    func execute()
}

protocol Dockable: class {
    associatedtype T
    func dock(object: T)
}

我已经为 Executable 实现了一个装饰器:

final class DockableExecutable: Executable, Dockable {
    typealias T = Executable
    private let decorated: Executable
    private var docked: Executable?
    init(_ decorated: Executable) {
        self.decorated = decorated
    }
    // from Executable
    func execute() {
        decorated.execute()
        docked?.execute()
    }
    // from Dockable
    func dock(object: Executable) {
        docked = object
    }
}

现在我不想在这样的课程中使用它:

final class MyViewController: UIViewController {
    init(save: Executable, uiUpdateConnector: Dockable<Executable>) {}
}

但这是不可能的,因为协议本身不是通用的,只有函数。编译器告诉我:

无法专门化非泛型类型“可停靠”

我们的想法是这样使用它:

let dockableExecutable = DockableExecutable(
    SQLUpdateExecutable(/** dependencies **/)
)
let controller = MyViewController(save: dockableExecutable, uiUpdateConnector: dockableExecutable)

Swift 3 中的正确语法如何让编译器满意?

更新 1

我使用以下代码取得了一些进展:

final class MyViewController: UIViewController {
    init<DOCKABLE: Dockable>(save: Executable, uiUpdateConnector: DOCKABLE) where DOCKABLE.T: Executable {}
}

看起来很奇怪,也许有人有更好的主意?现在使用该课程时,我得到:

无法推断通用参数“DOCKABLE”

所以我的问题没有改变:

Swift 3 中的正确语法如何让编译器满意?

更新 2

似乎不可能在基于协议的面向对象编程中使用 swift 通用(或更好:类型关联)协议。

所以我们必须将它们包装到某种容器中并松散基于协议的方法,或者我们必须为每种情况定义不同的协议。

因为我无法选择不使用协议,所以我必须编写不使用泛型的不同协议。羞愧于斯威夫特????

【问题讨论】:

  • 看看这个,看看它是否给你一个洞察力:How to create generic protocols in Swift? - Stack Overflow
  • 感谢您的链接,@leanne。但我认为这是一个不同的情况。我已经设法从通用协议派生(参见上面的 DockableExecutable 实现)。现在我尝试使用协议。
  • 您的链接描述了我在更新中编写的解决方案。因此,我认为这是解决方案的一部分。 (投票)
  • 另一个可以让您深入了解的链接:ios - Generics in Swift - "Generic parameter 'T' could not be inferred - Stack Overflow - 您的更新看起来很相似;但是,那里的 OP 已将协议添加到视图控制器的类签名中,或者在您的情况下:final class MyViewController: UIViewController, Dockable {
  • 您遇到的问题是protocols don't conform to themselves - 因此您不能将Executable 用作符合Executable 的类型。您可以将where 子句更改为where DOCKABLE.T == Executable,但那将只接受T is Executable 的Dockable 类型(不是符合T 的Executable )。

标签: swift oop generics


【解决方案1】:

我不确定是否可以在不删除类型的情况下解决您的问题,因为您不能将具有关联类型的协议用于变量类型或函数参数。

尝试使用类型擦除的解决方案:

final class AnyDockable<U>: Dockable {
    typealias T = U

    let _dock: (U) -> Void
    let _execute: () -> Void

    init<Base: Dockable & Executable>(base: Base) where Base.T == U {
        _dock = base.dock
        _execute = base.execute
    }

    func dock(object: U) {
        _dock(object)
    }

    func execute() {
        _execute()
    }
}

你的编译器会很高兴,我已经检查过了:

class Exe: Executable {
    func execute() {
        print("")
    }
}
let executable: Executable = Exe()

let de = AnyDockable(base: DockableExecutable(executable))

final class MyViewController: UIViewController {

    init(save: Executable, uiUpdateConnector: AnyDockable<Executable>) {
        super.init(nibName: "", bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

MyViewController(save: executable, uiUpdateConnector: de)

【讨论】:

  • 我必须做一些实验,直到我能告诉你它是否对我有帮助。在您当前的实现中,参数 uiUpdateConnector 不再是协议,而是具体类 AnyDockable。那违反了我的基于协议的面向对象方法。 AnyDockable 也非常具体地链接到 Executable。因此,如果我编写一个 DockableStringValue / DockableWhatever / .. 我还必须为所有这些类实现一个 AnyDockable 类。这打破了泛型的想法。通用的 Dockable 接口没有任何意义。你不觉得吗?
  • 我认为你是对的。这是不可能的:-/ - 为这部分答案“投票”,但我会稍等片刻接受它作为答案。也许一些天才告诉我们我们错了;-)
  • 是的,你是对的。 AnyDockable 应命名为 AnyDockableExecutable。我不认为类型擦除 AnyDockableExecutable 违反基于协议的设计,因为它的行为就像一个协议:所有实现细节仍然是隐藏的。实际上 Swift 标准库使用这种模式swiftdoc.org/v3.1/type/AnySequence
  • uiUpdateConnector 是否必须符合可执行协议?如果没有,则可以从 AnyDockable 中删除 Executable 部分,它的行为就像泛型类型(字符串等)。
【解决方案2】:

如果我没记错的话,您正在寻找 Protocol Composition(多协议一致性),不是吗?然后你可以使用不同的装饰器。

也许你正在寻找这个: Swift 3 protocol&lt;A, B&gt;,在 Swift 4 A &amp; B:

final class MyViewController: UIViewController {
    init(save: Executable, uiUpdateConnector: protocol<Dockable, Executable>) {}
}

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html

http://braking.github.io/require-conformance-to-multiple-protocols/

Multiple Type Constraints in Swift

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-30
    • 1970-01-01
    • 1970-01-01
    • 2010-10-22
    相关资源
    最近更新 更多