Swift:将对象数组转换为子类型

时间:2016-06-12 01:07:49

标签: swift

假设我有一系列动物,我想把它投射到Cats阵列中。在这里,Animal是Cat采用的协议。我喜欢像let cats: [Cat] = animals as! [Cat]这样的东西,但是编译中的这个段错误(顺便说一句,我在Linux Swift 3和Mac Swift 2.2上)。我的解决方法是创建一个单独向下转换每个项目并将其添加到新数组的函数(请参阅下面的小示例)。它会产生所需的结果,但不像我想的那样干净。

我的问题是:

  1. 这是完全愚蠢的,我只是错过了一个更简单的方法吗?

  2. 如何在下面的函数中传递一个类型作为目标参数,而不是传递实例? (例如,我想传递Cat.self而不是Cat(id:0),但这样做会导致错误,说无法将Cat.Type转换为预期的参数类型Cat)

  3. 这是我到目前为止所拥有的:

    protocol Animal: CustomStringConvertible 
    {
        var species: String {get set}
        var id: Int {get set}
    }
    
    extension Animal
    {
        var description: String 
        {
            return "\(self.species):\(self.id)"
        }
    }
    
    class Cat: Animal 
    {
        var species = "felis catus"
        var id: Int
    
        init(id: Int)
        {
            self.id = id
        }
    }
    
    func convertArray<T, U>(_ array: [T], _ target: U) -> [U]
    {
        var newArray = [U]()
        for element in array
        {
            guard let newElement = element as? U else
            {
                print("downcast failed!")
                return []
            }
    
            newArray.append(newElement)
        }
    
        return newArray
    }
    
    let animals: [Animal] = [Cat(id:1),Cat(id:2),Cat(id:3)]
    print(animals)
    print(animals.dynamicType)
    
    // ERROR: cannot convert value of type '[Animal]' to specified type '[Cat]'
    // let cats: [Cat] = animals
    
    // ERROR: seg fault
    // let cats: [Cat] = animals as! [Cat]
    
    let cats: [Cat] = convertArray(animals, Cat(id:0))
    print(cats)
    print(cats.dynamicType)
    

2 个答案:

答案 0 :(得分:3)

  
      
  1. 我错过了一种更简单的方法吗?
  2.   

您可以使用map进行转换:

let cats: [Cat] = animals.map { $0 as! Cat }
  
      
  1. 如何在下面的函数中传递类型作为目标参数,而不是传递实例?
  2.   

首先,您需要删除实例参数:

func convertArray<T, U>(array: [T]) -> [U] {
    var newArray = [U]()
    for element in array {
        guard let newElement = element as? U else {
            print("downcast failed!")
            return []
        }
        newArray.append(newElement)
    }
    return newArray
}

由于您无法显式指定类型参数,因此需要为编译器提供一些信息以推断U的类型。在这种情况下,您需要做的就是说您将结果分配给Cat数组:

let cats: [Cat] = convertArray(animals)

答案 1 :(得分:2)

从Swift 4.1开始,使用compactMap是首选方法,假设您不希望当数组中有任何其他Animal(例如Dog)时该方法完全失败(并且实际上崩溃)

let animals: [Animal] = [Cat(id:1),Dog(id:2),Cat(id:3)]
let cats: [Cat] = animals.compactMap { $0 as? Cat }

因为compactMap将清除所有nil值,所以最终得到的数组如下:

[Cat(1), Cat(3)]

作为奖励,使用此循环而不是for循环,您还将获得一些性能改进。