Swift:转换集合,并创建自定义可转换协议

时间:2015-01-02 23:14:00

标签: swift

考虑这个Person类,它只是实现StringLiteralConvertible并将字符串文字分配给name

class Person : StringLiteralConvertible {
    var name : String?

    typealias StringLiteralType = String

    required init(stringLiteral value: StringLiteralType) {
        println("stringLiteral \(value)")
        name = value
    }

    typealias ExtendedGraphemeClusterLiteralType = String

    required init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) {
        println("extendedGraphemeClusterLiteral \(value)")
        name = value
    }

    typealias UnicodeScalarLiteralType = Character

    required init(unicodeScalarLiteral value: UnicodeScalarLiteralType) {
        println("unicodeScalarLiteral \(value)")
        name = "\(value)"
    }
}

这允许我使用字符串创建Person实例:

let aaron : Person = "Aaron"

我甚至可以从字符串数组中转换Person s数组:

let names = ["John", "Jane"] as [Person]

但是这仅适用于字符串文字。如果我使用字符串变量,它将失败:

let aaronString = "Aaron"
let aaron : Person = aaronString
// Error: 'NSString' is not a subtype of 'Person'

类似地,尝试强制转换非文字字符串数组失败:

let nameStrings = ["John", "Jane"]
let people : [Person] = nameStrings
// Error: 'String' is not identical to 'Person'

我有三个问题:

  1. 我可以实现另一个协议来将非文字字符串转换为Person吗?我想这样做,所以我可以转换整个集合来转换对象。

  2. 如果不是#1,map +初始化程序是自己执行转换的最佳方式吗?

    let nameStrings = ["John", "Jane"]
    let people = nameStrings.map{Person(name: $0)}
    
  3. 如果是#1,是否有类似的方法可用于指定转换两个在层次结构中无关的对象的方法?也就是说,如果没有初始化程序,我可以解决此错误吗?

    let rikerPerson : Person = "Riker"
    let rikerEmployee = rikerPerson as Employee
    // Error: 'Person' is not convertible to 'Employee'
    

1 个答案:

答案 0 :(得分:3)

  1. 你所描述的“铸造”并不是真正的铸造(比如,s = “fred”; ns = s as NSString,或者是用C ++铸造的)。

    let names = ["John", "Jane"] as [Person]
    

    只是另一种写作方式:

    let names: [Person] = ["John", "Jane"]
    

    即告诉Swift使用StringLiteralConvertible的许多可能版本中的哪一个(而不是String的版本,这是默认值)。

    换句话说 - 您的as正在履行与此代码段中as类似的功能,该功能消除了两个仅因返回类型而不同的重载函数的歧义:

    func f() -> String { return "foo" }
    func f() -> Int { return 42 }
    
    let i = f() as Int    // i will be 42
    let s = f() as String // s will be “foo"
    

    此处没有“转化” - as仅用于消除哪些f Swift电话的歧义。与选择init(stringLiteral:)的情况相同。

  2. 绝对(但只有在map{ }之间放置一个空格;-)。

    如果您担心将所有内容转换为数组的浪费只是为了做其他事情,请查看lazy(a).map

  3. 不。在测试版中,曾经有一个__conversion() -> T方法可以在你自己的类上实现这样的“强制转换” - 或者更重要的是,允许你将Person类传递给一个函数一个Employee参数,并将其隐式转换。但那消失了。一般来说,这种隐式转换与Swift的风格是对立的,除了在极少数情况下(Obj-C和C interop,以及选择性中的隐式包装,是主要的)。您必须为init写一个Employee,其中Person(或Person符合的某个类或协议),然后调用它。

    < / LI>