3

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

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

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()

我明白为什么它不能编译: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使用关联类型。所以它不能被提取到另一个协议。

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

class Protocol<T> {
    func sayHello(name: String = "World") {
        print("Hello, \(name)!")
    }
}

class Class<T>: Protocol<T> {
    override func sayHello(name: String = "Stack Overflow") {
        super.sayHello(name)
    }
}

Class<()>().sayHello()
4

2 回答 2

4

您正在尝试重新发明协议中的继承,但没有这样的事情。但是得到你在说什么是微不足道的。只是说你的意思。你的意思不是“我想做我继承的事情”。你的意思是“我想做一些常见的行为”。只需为该常见行为提供一个名称。这消除了您所指的所有歧义。

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 目前还没有解决方案。

于 2016-08-27T18:15:23.967 回答
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!

这确实符合我的需求,但没有回答这个问题。所以我不是100%满意。
我相信 Rust 通过允许特征/协议在使用X<T>类型和关联类型中都是通用的来做到这一点。

于 2016-08-27T21:24:16.570 回答