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

Swift:将对象数组强制转换为子类型数组,swift,Swift,假设我有一组动物,我想把它扔给一组猫。在这里,动物是猫采用的协议。我想要的东西像让猫:[猫]=动物一样![Cat]但是这个seg在编译时出错(顺便说一句,我同时使用Linux Swift 3和Mac Swift 2.2)。我的解决方法是只创建一个函数,该函数可以逐个地删除每个项并将其添加到新数组中(请参见下面的小示例)。它产生了期望的结果,但没有我想要的那么干净 我的问题是: 这完全是愚蠢的,而我只是错过了一个更简单的方法 如何在下面的函数中传递类型作为目标参数,而不是传递实例?(例如,我希望传

假设我有一组动物,我想把它扔给一组猫。在这里,动物是猫采用的协议。我想要的东西像
让猫:[猫]=动物一样![Cat]
但是这个seg在编译时出错(顺便说一句,我同时使用Linux Swift 3和Mac Swift 2.2)。我的解决方法是只创建一个函数,该函数可以逐个地删除每个项并将其添加到新数组中(请参见下面的小示例)。它产生了期望的结果,但没有我想要的那么干净

我的问题是:

  • 这完全是愚蠢的,而我只是错过了一个更简单的方法

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

  • 以下是我目前掌握的情况:

    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)
    
    协议动物:CustomStringConverable
    {
    变量种类:字符串{get set}
    变量id:Int{get set}
    }
    扩展动物
    {
    变量说明:字符串
    {
    返回“\(self.species):\(self.id)”
    }
    }
    类别猫:动物
    {
    var species=“猫科动物”
    变量id:Int
    init(id:Int)
    {
    self.id=id
    }
    }
    func convertArray(array:[T],target:U)->[U]
    {
    var newArray=[U]()
    对于数组中的元素
    {
    guard let newElement=元素为?U else
    {
    打印(“向下播放失败!”)
    返回[]
    }
    追加(新元素)
    }
    返回新数组
    }
    让动物:[动物]=[猫(id:1)、猫(id:2)、猫(id:3)]
    印刷品(动物)
    打印(动物。动态类型)
    //错误:无法将类型“[Animal]”的值转换为指定的类型“[Cat]”
    //让猫[猫]=动物
    //错误:seg故障
    //让猫[猫]=动物当宠物![猫]
    let cats:[Cat]=convertArray(动物,猫(id:0))
    印刷品(猫)
    打印(CAT.dynamicType)
    
  • 我是否错过了一个更简单的方法
  • 您可以使用
    map
    进行转换:

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

    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
    }
    

    从Swift 4.1开始,使用
    compactMap
    将是首选方法,假设您不希望该方法在阵列中有任何其他动物(例如狗)时完全失败(并实际崩溃)

    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
    循环来获得一些性能改进。

    非常感谢,如果对我有效,我将尝试删除U并接受此选项。我曾想过地图,但在实践中,我会扔如果沮丧fails@Philip就掷骰子而言,你可以在
    地图中做同样的事情。操作员
    as也会在不成功的施法中抛出。哦,我没想到map会接受一个抛出的闭包。是的,让我来决定!抛出可能会起作用,但我希望有一个自定义消息与之一起使用
    for
    循环不是性能问题。问题在于没有事先分配足够的容量。
    [Cat(1), Cat(3)]