在Swift中将抽象类型转换为混凝土类型

时间:2017-04-12 08:12:05

标签: swift generics type-erasure swift-protocols associated-types

我正在尝试为我的应用制作数据模型。这是场景:

我的应用包含客户模型,其中包含客户的信息,还包含他/她的付款来源。 API为我提供了两种付款来源:银行帐户,它们有完全不同的字段。

所以,这是我的问题,我希望有抽象类型,即PaymentSource,然后在每个PaymentSource中都有一个函数来返回转换为它的类型的对象。一些我如何删除类型。

我需要将我的抽象类型放在一个框中,并将其用作具体类型(AnyPaymentSource)。

所以,我做了如下:

protocol PaymentSource {
    associatedtype Kind
    func cast() -> Kind
}

struct AnyPaymentSource<PS: PaymentSource> {
    private var paymentSource: PS
    init(paymentSource: PS) {
        self.paymentSource = paymentSource
    }
    func cast() -> PS.Kind {
        return paymentSource.cast()
    }
}

struct Card: PaymentSource {
    func cast() -> Card {
        return self
    }
}

struct BankAccount: PaymentSource {
    func cast() -> BankAccount {
        return self
    }
}

struct Customer { 
    var firstName: String
    var lastName: String
    var email: String
    var paymentSource : AnyPaymentSource<PaymentSource> 
}

Customer给出了以下说明错误:

  

不支持使用'PaymentSource'作为符合协议'PaymentSource'的具体类型

我在哪里做错了?

2 个答案:

答案 0 :(得分:1)

斯威夫特是statically typed language。这意味着必须在编译时知道变量的类型。

当我遇到这个问题时,我解决了这个问题

protocol PaymentSource {
    associatedtype Kind
    func cast() -> Kind
}

struct AnyPaymentSource<PS: PaymentSource> {
    private var paymentSource: PS
    init(paymentSource: PS) {
        self.paymentSource = paymentSource
    }
    func cast() -> PS.Kind {
        return paymentSource.cast()
    }
}

struct Card: PaymentSource {
    func cast() -> Card {
        return self
    }
}

struct BankAccount: PaymentSource {
    func cast() -> BankAccount {
        return self
    }
}

struct Customer<T:PaymentSource> {
    var firstName: String
    var lastName: String
    var email: String
    var paymentSource : AnyPaymentSource<T>
}
func test(){
    let customerWithCard = Customer<Card>(
        firstName: "",
        lastName: "",
        email: "",
        paymentSource: AnyPaymentSource(paymentSource: Card())
    )
    let customerWithBankAccount = Customer<BankAccount>(
        firstName: "",
        lastName: "",
        email: "",
        paymentSource: AnyPaymentSource(paymentSource: BankAccount())
    )
    print(customerWithCard.paymentSource.cast())
    print(customerWithBankAccount.paymentSource.cast())
    return
}

答案 1 :(得分:1)

如果你想要实现的是@Andrew Ashurov在他的回答中提到的,那么就没有必要实施AnyPaymentSource。如Swift Protocols Documentation中所述:

  

协议实际上并不实现任何功能。   尽管如此,您创建的任何协议都将成为完全成熟的类型   用于您的代码

意味着已经能够将协议视为类型

可能是:

protocol PaymentSource {
    func cast() -> Self
}

struct Card: PaymentSource {
    func cast() -> Card {
        return self
    }
}

struct BankAccount: PaymentSource {
    func cast() -> BankAccount {
        return self
    }
}

struct Customer {
    var firstName: String
    var lastName: String
    var email: String
    var paymentSource : PaymentSource?
}

创建客户:

let cardCustomer = Customer(firstName: "Card Fname", lastName: "Card Lname", email: "cardemail@example.com", paymentSource: Card())

let bankAccountCustomer = Customer(firstName: "Bank Account Fname", lastName: "Bank Account Lname", email: "bankaccountemail@example.com", paymentSource: BankAccount())

请注意,在Customer struct,paymentSource类型的PaymentSource属性中,这意味着它可以指定为符合PaymentSource协议(Card和在你的情况下BankAccount