【问题标题】:Generic class initialization from protocol来自协议的通用类初始化
【发布时间】:2020-07-19 21:40:09
【问题描述】:

我想要类对象JSONSerialization。所以我的输入是[String: Any],从文档中我知道它可能是NSNullNSStringNSNumber。所以我制定了一个协议:

protocol PlainValue { }

以上都符合这个协议:

extension NSString: PlainValue { }
extension NSNull: PlainValue { }
extension NSNumber: PlainValue { }

然后我想创建一个保存键值的类存储,例如:

class KeyValue<T: PlainValue> {
    let key: NSString
    let value: T
    
    init(key: NSString, value: T) {
        self.key = key
        self.value = value
    }
}

并且想这样使用它:

func parse(json: [String: Any]) {
    ...
    if let value = json[key] as? PlainValue { // this should be `Any` but I want to check here if thats an PlainValue or embedded object
        let obj = KeyValue<PlainValue>(key: key, value: value) // this currently not working
        ...
    }
    ...
}

但我的问题是如何在协议级别声明此对象创建。如果我这样做:

protocol PlainValue {
    func convert(key: NSString) -> KeyValue<PlainValue>
}

我收到错误:

协议类型“PlainValue”的值不能符合“PlainValue”;只有结构/枚举/类类型可以符合协议

有道理,因为我已经在协议声明中。所以我有一种感觉,也许这应该在KeyValue&lt;T&gt; 级别上声明?但我不确定我是否走在正确的道路上,因为我在尝试采用的每一种方法上都会遇到编译器错误。谁能指出我正确的方向如何使它工作?

我觉得最接近工作的方法是:

extension NSNumber: PlainValue {
    func convert(key: NSString) -> KeyValue<NSNumber> {
        return KeyValue<NSNumber>(key: key, value: self)
    }
}

NSStringNSNull 上与上述类似,但不确定如何在协议级别声明它以使其可以从我的解析函数中调用。自从

protocol PlainValue: JSONValue { 
    func convert(key: NSString) -> KeyValue<Self> // this claims that implementation does not match this declaration

    func convert<T: PlainValue>(key: NSString) -> KeyValue<T> // same as above

    associatedtype Object: PlainValue
    func convert(key: NSString) -> KeyValue<Object> // this is working! But... then I cannot check if value is PlainValue in my parsing function, because of `Protocol 'PlainValue' can only be used as a generic constraint because it has Self or associated type requirements`
}

【问题讨论】:

    标签: ios swift


    【解决方案1】:

    一个协议不能符合另一个协议,KeyValue&lt;T&gt; 中的泛型类型T 必须是具体类型。

    另一种选择是具有关联类型的协议扩展

    extension NSString: PlainValue { }
    extension NSNull: PlainValue {  }
    extension NSNumber: PlainValue {  }
    
    protocol PlainValue {
        associatedtype ValueType : PlainValue = Self
        func convert(key: NSString) -> KeyValue<ValueType>
    }
    
    extension PlainValue where ValueType == Self {
        func convert(key: NSString) -> KeyValue<ValueType> {
            return KeyValue<ValueType>(key: key, value: self)
        }
    }
    
    
    class KeyValue<T: PlainValue> {
        let key: NSString
        let value: T
        
        init(key: NSString, value: T) {
            self.key = key
            self.value = value
        }
    }
    

    let stringResult = "Foo".convert(key:"bar")
    let numberResult = NSNumber(value:12).convert(key:"baz")
    let nullResult = NSNull().convert(key:"buz")
    

    【讨论】:

    • 谢谢,这是我正在寻找的解释! associatedtype ValueType : PlainValue = Self 搞定了! (抱歉还不能投票 - 我是新来的;))`
    【解决方案2】:

    不确定我是否正确理解您想要实现的目标,但根据您所写的,您在这里不一定需要泛型。如果在您的情况下没有其他因素需要泛型,下面的代码将解决您的问题。此外,这样您就不需要在协议级别声明对象创建。

    class KeyValue {
        let key: NSString
        let value: PlainValue
    
        init(key: NSString, value: PlainValue) {
            self.key = key
            self.value = value
        }
    }
    

    您的原始示例不起作用,因为您需要一个符合 PlainValue 的具体类型。在swift中,只有结构/枚举/类等具体类型才能符合协议。例如,let obj = KeyValue&lt;NSString&gt;(key: key, value: value) 可以在那里工作,而使用 PlainValue 则不行。

    【讨论】:

    • 哦,是的,谢谢!编译,但tbf我也很好奇为什么像我试图做的那样声明它不起作用:)
    • 正如编译错误所说“'PlainValue' 不能符合 'PlainValue'”。你需要一个符合 PlainValue 的类型。例如: let obj = KeyValue(key: key, value: value) 可以正常工作。
    • 而上面的原因是,在 swift 中只有具体的类型如 struct/enum/class 可以符合协议。这就是为什么它不适用于原始示例中的 PlainValue。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-23
    • 2020-05-17
    • 1970-01-01
    • 1970-01-01
    • 2016-08-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多