【问题标题】:Swift generics: return type based on parameter typeSwift 泛型:基于参数类型的返回类型
【发布时间】:2016-01-04 12:48:16
【问题描述】:

假设我有一个从公共超类继承的对象集合(在这种情况下,这比协议更可取):

class ObjectSuperClass {
    type: ObjectType
}
class ObjectClass1: ObjectSuperClass {
    type = .Type1
}
class ObjectClass2: ObjectSuperClass {
    type = .Type2
}

我正在寻找这样的通用搜索功能:

func objectsOfType<T: ObjectSuperClass>(T.class, otherFilter: Any?) -> [T]

这可用于搜索给定的子类型,返回更具体的结果数组:

let result = objectsOfType(ObjectClass2.class, otherFilter: nil) -> [ObjectClass2]
(pseudo-swift)

我觉得这是泛型可以提供帮助的地方,但看不到应该在哪里放置约束。有可能吗?

【问题讨论】:

    标签: swift generics


    【解决方案1】:

    这很有效……

    func filterType<T>(list: [AnyObject]) -> [T]
    {
        return list.filter{ $0 is T }.map{ $0 as! T }
    }
    

    ...如果您将结果分配给已明确键入的内容,如下例所示:

    class ObjectSuperClass: CustomStringConvertible
    {
        let myType: String
        init(aString: String)
        {
            myType = aString
        }
        var description: String { return myType }
    }
    
    class ObjectClass1: ObjectSuperClass
    {
        init()
        {
            super.init(aString: "<t 1>")
        }
    }
    
    class ObjectClass2: ObjectSuperClass
    {
        init()
        {
            super.init(aString: "<t 2>")
        }
    }
    
    let unfilteredList: [AnyObject] = [ ObjectClass1(), ObjectClass2(), ObjectSuperClass(aString: "<Who knows>")]
    
    let filteredList1: [ObjectClass1] = filterType(list: unfilteredList)
    print("\(filteredList1)") // <t 1>
    
    let filteredList2: [ObjectClass2] = filterType(list: unfilteredList)
    print("\(filteredList2)") // <t 2>
    
    let filteredList3: [ObjectSuperClass] = filterType(list: unfilteredList)
    print("\(filteredList3)") // [<t 1>, <t 2>, <Who knows>]
    

    T 在每种情况下都是从请求的返回类型中推断出来的。函数本身根据元素是否为所需类型过滤原始数组,然后将过滤后的结果强制转换为正确的类型。


    如果你想要一个“额外的过滤器”,你不需要显式地输入结果,只要 T 可以从你的额外过滤器函数中推断出来。

    func extraFilterType<T>(list: [AnyObject], extraFilter: T -> Bool) -> [T]
    {
        return list.filter{ $0 is T }.map{ $0 as! T }.filter(extraFilter)
    }
    
    let filteredList = extraFilterType(unfilteredList){
        (element : ObjectClass2) -> Bool in
        !element.description.isEmpty
    }
    
    print("\(filteredList)") // <t 2>
    

    编辑

    filterType 函数的更简洁版本将使用 flatMap()

    func filterType<T>(list: [Any]) -> [T]
    {
        return list.flatMap{ $0 as? T }
    }
    

    编辑 2

    Flatmap 已被弃用,因为 Swift 4.something,使用 compactMap

    func filterType<T>(list: [Any]) -> [T]
    {
        return list.compactMap{ $0 as? T }
    }
    

    【讨论】:

    • 这很好,+1。出于好奇,你能利用.dynamicType 属性甚至只对超类的对象进行排序吗?例如,在上面的示例中,filteredList3 只产生[&lt;Who knows&gt;]? (例如return list.filter{ $0.dynamicType == T.self }.map{ $0 as! T } 不起作用,但类似的东西?)
    • @dfri 可能。我没有检查。这不是我通常会担心的事情,因为如果我不能将子类的对象视为基类的对象,我会将其视为代码异味,表明我的类设计是错误的。
    • @dfri 实际上它有效。您只需将第一个过滤器闭包的参数显式声明为AnyObject,即return list.filter{ (anObj: AnyObject) -&gt; Bool in anObj.dynamicType == T.self }.map{ $0 as! T }
    • 我想我可以补充一点,如果我们想要使用这里讨论的替代版本(对于“完美类型匹配”),我们可能最好使用 @ 987654335@ 而不是 AnyObject。对于后者,非类类型(如Int)将被表示为obj-c类(如Int__NSCFNumber.Type),在比较.dynamicType与过滤器类型@时将找不到我们发送 987654341@(例如,如果我们发送 Int),而对于 Any,例如Int 实例将被存储为Int 类型在[Any] 数组中。但是,在进行“不太精确的类型检查”$0 is T 时,这不是问题。
    • @dfri 刚刚重新审视了这个问题,因为有人支持它。你是对的Any。此外,我们可以使用flatMap 来消除对显式过滤器的需求。
    【解决方案2】:

    这是我能想到的最接近的近似值:

    func objectsOfType<T: ObjectSuperClass>(type type: T.Type) -> [T] {
        // Just returns an array of all objects of given type
    }
    
    func objectsOfType<T: ObjectSuperClass>(type type: T.Type, predicate: T -> Bool) -> [T] {
        // Uses predicate to filter out objects of given type
    }
    

    用法:

    let bar = objectsOfType(type: ObjectClass1.self)
    
    let baz = objectsOfType(type: ObjectClass2.self) {
        // Something that returns Bool and uses $0
    }
    

    从技术上讲,您也可以在上面不使用 type 参数,但是您需要有明确类型的接收器(在上面的示例中为 barbaz),以便 Swift 可以正确推断类型并使用正确版本的泛型函数。

    【讨论】:

      【解决方案3】:

      你可以这样实现函数:

      func objectsOfType<T: ObjectSuperClass>(objects: [ObjectSuperClass], subclass: T.Type, otherFilter: (T->Bool)?) -> [T] {
          if let otherFilter = otherFilter {
              return objects.filter{$0 is T && otherFilter($0 as! T)}.map{$0 as! T}
          } else {
              return objects.filter{$0 is T}.map{$0 as! T}
          }
      }
      

      使用示例:

      objectsOfType(arrayOfObjects, subclass: ObjectClass1.self, otherFilter: nil)
      

      请注意,我不喜欢强制施法,但在这种情况下,它应该不会造成问题。

      或者,更详细的函数版本,更少强制转换:

      func objectsOfType<T: ObjectSuperClass>(objects: [ObjectSuperClass], subclass: T.Type, otherFilter: (T->Bool)?) -> [T] {
          return objects.filter({object in
              if let object = object as? T {
                  if let otherFilter = otherFilter {
                      return otherFilter(object)
                  } else {
                      return true
                  }
              } else {
                  return false
              }
          }).map({object in
              return object as! T
          })
      }
      

      【讨论】:

        猜你喜欢
        • 2023-03-30
        • 2016-11-13
        • 1970-01-01
        • 1970-01-01
        • 2021-11-10
        • 1970-01-01
        • 1970-01-01
        • 2020-07-04
        • 1970-01-01
        相关资源
        最近更新 更多