如何在Swift通用函数中创建正确类型的新对象?

时间:2017-07-17 23:27:43

标签: generics swift3

我有以下Swift通用方法:

  static func getRecords<T>(_ which: Which, from table: Table, completionHandler: @escaping ([T]) -> Void)
  {
    let url: String = DatabaseInterface.getUrl(which, from: table)
    Http.get(url: url)
    {
      response, error in
      if nil != error
      {
        print("get\(table.rawValue)() failed; error = \(error!)")
        return
      }

      var objects: [T] = []
      let formatter = DateFormatter()
      formatter.dateFormat = "yyyy-MM-dd" // Matches PHP's "Y-m-d" \Date format.

      let records = (response!["records"])!
      for record in (records as! [NSDictionary])
      {
        // This line fails with the error
        // 'T' cannot be constructed because it has no accessible initializers
        objects.append(T(fromDictionary: record as! [String: Any]))
      }
      DispatchQueue.main.async
      {
        completionHandler(objects)
      }
    }
  }

Http.get()方法从服务器检索记录并将JSON解码为字典数组。

objects.append()行因此错误而失败:

  

'T'无法构造,因为它没有可访问的初始值设定项

我的第一个问题:是否可以在泛型函数中创建由类型占位符指定的类型的新实例?

我的第二个问题:如果有可能,这样做的正确语法是什么?

1 个答案:

答案 0 :(得分:0)

编译器不知道T有一个初始化器。您需要将其约束为包含init(fromDictionary:)

的协议或类
protocol JSONInitializable {
    init(fromDictionary: [String: Any])
}

static func getRecords<T: JSONInitializable>(
    _ which: Which,
    from table: Table,
    completionHandler: @escaping ([T]) -> Void
    ) {
    let url = DatabaseInterface.getUrl(which, from: table)
    Http.get(url: url) { _response, error in
    guard let response = _response as? [String: [[String: Any]]],
        error == nil else {
        print("get\(table.rawValue)() failed; error = \(error!)")
        return
    }

    guard let objects = response["records"]?.map(T.init(fromDictionary:)) {
        fatalError("`response` doesn't have a value for the key \"records\".")
    }

    DispatchQueue.main.async { completionHandler(objects) }
}