当协议具有关联类型时,从常规方法调用协议默认实现

时间:2016-08-27 17:00:03

标签: swift protocols associated-types default-implementation

我有一个带有默认参数的静态方法的协议。我想更改实现协议的类中的默认值。基本上做的是用类和超级容易做的事 当协议没有关联类型时,我只有一个解决方案。

以下代码有效,但只要取消注释关联的类型声明,它就不会编译。

protocol Protocol {
//    associatedtype AssociatedType
}

extension Protocol {
    func sayHello(name: String = "World") {
        print("Hello, \(name)!")
    }
}

class Class<T>: Protocol {
    typealias AssociatedType = T

    func sayHello(name: String = "Stack Overflow") {
        // Uncommenting the Protocol.AssociatedType causes:
        // Protocol can only be used as a generic constraint because it has associated type requirements
        (self as Protocol).sayHello(name)
    }
}

Class<()>().sayHello()

我明白为什么它不能编译:{{1​​}}没有Protocol的具体类型 所以也许问题应该是“我能明确地专门化一个协议吗?”,我认为答案是否定的。

我有一个部分解决方法。但即使它起作用,它也很糟糕 特别是当你考虑我正在编写一个公共AssociatedType的库时,所以下面的解决办法迫使我有了第二个协议,它必须是公共的,但是没用。 这是解决方法:

sayHello

但这对我不起作用,因为我的protocol Parent {} protocol Protocol: Parent { associatedtype AssociatedType } extension Parent { func sayHello(name: String = "World") { print("Hello, \(name)!") } } class Class<T>: Protocol { typealias AssociatedType = T func sayHello(name: String = "Stack Overflow") { (self as Parent).sayHello(name) } } Class<()>().sayHello() 使用了相关的类型。所以它不能被提取到另一个协议。

为了确保我很清楚,这就是我想要的,只是用课程代替协议:

sayHello

2 个答案:

答案 0 :(得分:2)

你试图在协议中重新发明继承,而且没有这样的东西。得到你所说的话是微不足道的;只是说出你的意思。你的意思并不是“我想做我继承的事情。”你的意思是“我想做一些共同的行为。”只需提供该常见行为的名称即可。这消除了关于你的意思的所有歧义。

protocol Protocol {
        associatedtype AssociatedType
}

extension Protocol {
    // Put the default behavior on the protocol, not on the instance
    // Of course you could also put it on the instance if that were convenient.
    static func defaultSayHello(_ name: String = "World") {
        print("Hello, \(name)!")
    }

    // If you want a default on the instance, too, provide one that we an override
    func sayHello(_ name: String = "World") {
        Self.defaultSayHello(name)
    }
}

class Class<T>: Protocol {
    typealias AssociatedType = T

    func sayHello(name: String = "Stack Overflow") {
        // Now the default behavior lives on my type
        Class.defaultSayHello(name)
    }
}

// But other types can get default behavior
class OtherClass<T>: Protocol {
    typealias AssociatedType = T
}

Class<()>().sayHello() // Hello, Stack Overflow!
OtherClass<()>().sayHello() // Hello, World!

令人沮丧的是,Swift没有办法将defaultSayHello限制为Protocol的实施者。所以在技术上任何人都可以称之为。它有时值得用_作为前缀,以表明外人不应该。这是协议中的基本访问控制问题,与此特定问题无关;当你想要“我的实施者可以自己使用的东西,而不应该被随机调用”时,它会一直出现。 Swift今天没有解决方案。

答案 1 :(得分:1)

受Rob Napier的回答启发,这就是我的目标;默认情况下旧的重载:

protocol Protocol {
    associatedtype AssociatedType
}

extension Protocol {
    func sayHello(name: String = "World") {
        print("Hello, \(name)!")
    }
}

class Class<T>: Protocol {
    typealias AssociatedType = T

    func sayHello() {
        self.sayHello("Stack Overflow")
    }
}

Class<()>().sayHello()      // Hello, Stack Overflow!
Class<()>().sayHello("you") // Hello, you!

这确实符合我的需要,但没有回答这个问题。所以我不是百分百满意 我相信Rust通过使用X<T>和相关类型允许特征/协议是通用的来实现这一点。