无法在Swift的另一个协议中将协议用作关联类型

发布于 2021-01-31 23:57:04

我有一个协议,Address该协议继承自另一个协议Validator,并且Address满足Validator扩展要求。

还有另一种协议,FromRepresentable其要求associatedTypeValueWrapper)应该为Validator

现在,如果我尝试使用Addressas associatedType,那么它将无法编译。它说,

推断的类型“地址”(通过匹配要求“ valueForDetail”)无效:不符合“ Validator”。

这种用法非法吗?我们不能像所有的那样用Address它代替。Validator``Addresses``Validator

下面是我正在尝试的代码。

enum ValidationResult {
    case Success
    case Failure(String)
}

protocol Validator {
    func validate() -> ValidationResult
}

//Address inherits Validator
protocol Address: Validator {
    var addressLine1: String {get set}
    var city: String {get set}
    var country: String {get set}
}

////Fulfill Validator protocol requirements in extension
extension Address {
    func validate() -> ValidationResult {
        if addressLine1.isEmpty {
            return .Failure("Address can not be empty")
        }
        return .Success
    }
}

protocol FormRepresentable {
    associatedtype ValueWrapper: Validator
    func valueForDetail(valueWrapper: ValueWrapper) -> String
}


// Shipping Address conforming to Address protocol. 
// It should also implicitly conform to Validator since
// Address inherits from Validator?
struct ShippingAddress: Address {
    var addressLine1 = "CA"
    var city = "HYD"
    var country = "India"
}


// While compiling, it says:
// Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform
// to 'Validator'.
// But Address confroms to Validator.
enum AddressFrom: Int, FormRepresentable {
    case Address1
    case City
    case Country

    func valueForDetail(valueWrapper: Address) -> String {
        switch self {
        case .Address1:
            return valueWrapper.addressLine1
        case .City:
            return valueWrapper.city
        case .Country:
            return valueWrapper.country
        }
    }
}

更新: 提交了一个错误。

关注者
0
被浏览
260
1 个回答
  • 面试哥
    面试哥 2021-01-31
    为面试而生,有面试问题,就找面试哥。

    David已经提到的问题是,一旦将协议限制associatedtype为特定的(非@objc)协议,就必须使用具体的类型来满足该要求。

    这是因为协议不符合自己-所以这意味着你不能使用Address,以满足类型的协议的关联型需求符合Validator,因为Address不是
    一个类型符合Validator

    如我在此处的答案所示,请考虑以下反例:

    protocol Validator {
        init()
    }
    protocol Address : Validator {}
    
    protocol FormRepresentable {
        associatedtype ValueWrapper: Validator
    }
    
    extension FormRepresentable {
        static func foo() {
            // if ValueWrapper were allowed to be an Address or Validator,
            // what instance should we be constructing here?
            // we cannot create an instance of a protocol.
            print(ValueWrapper.init())
        }
    }
    
    // therefore, we cannot say:
    enum AddressFrom : FormRepresentable {
        typealias ValueWrapper = Address
    }
    

    最简单的解决方案是放弃关联类型Validator上的协议约束ValueWrapper,从而允许您在方法参数中使用抽象类型。

    protocol FormRepresentable {
        associatedtype ValueWrapper
        func valueForDetail(valueWrapper: ValueWrapper) -> String
    }
    

    enum AddressFrom : Int, FormRepresentable {
    
        // ...
    
        func valueForDetail(valueWrapper: Address) -> String {
            // ...
        }
    }
    

    如果您需要关联的类型约束,并且每个AddressFrom实例仅期望一个具体的实现Address作为输入–您可以使用泛型,以便AddressFrom使用在方法中使用的给定具体地址类型进行初始化。

    protocol FormRepresentable {
        associatedtype ValueWrapper : Validator
        func valueForDetail(valueWrapper: ValueWrapper) -> String
    }
    

    enum AddressFrom<T : Address> : Int, FormRepresentable {
    
        // ...
    
        func valueForDetail(valueWrapper: T) -> String {
            // ...
        }
    }
    

    // replace ShippingAddress with whatever concrete type you want AddressFrom to use
    let addressFrom = AddressFrom<ShippingAddress>.Address1
    

    但是,如果您同时需要关联的类型约束, 并且
    每个AddressFrom实例必须能够处理任何类型的输入,则Address必须使用类型擦除来将任意Address类型包装为具体类型。

    protocol FormRepresentable {
        associatedtype ValueWrapper : Validator
        func valueForDetail(valueWrapper: ValueWrapper) -> String
    }
    

    struct AnyAddress : Address {
    
        private var _base: Address
    
        var addressLine1: String {
            get {return _base.addressLine1}
            set {_base.addressLine1 = newValue}
        }
        var country: String {
            get {return _base.country}
            set {_base.country = newValue}
        }
        var city: String {
            get {return _base.city}
            set {_base.city = newValue}
        }
    
        init(_ base: Address) {
            _base = base
        }
    }
    

    enum AddressFrom : Int, FormRepresentable {
    
        // ...
    
        func valueForDetail(valueWrapper: AnyAddress) -> String {
            // ...
        }
    }
    

    let addressFrom = AddressFrom.Address1
    
    let address = ShippingAddress(addressLine1: "", city: "", country: "")
    
    addressFrom.valueForDetail(AnyAddress(address))
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看