Jea*_*non 136 generics type-inference swift
我有一个泛型函数调用Web服务并将JSON响应序列化回一个对象.
class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: AnyClass, completionHandler handler: ((T) -> ())) {
/* Construct the URL, call the service and parse the response */
}
Run Code Online (Sandbox Code Playgroud)
我想要完成的是相当于这个Java代码
public <T> T invokeService(final String serviceURLSuffix, final Map<String, String> params,
final Class<T> classTypeToReturn) {
}
Run Code Online (Sandbox Code Playgroud)
AnyClass作为参数类型正确的事情?MyObject.self作为returnsClass值传递,但是我收到编译错误"无法将表达式的类型'()'转换为'String'"CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) { cityInfo in /*...*/
}
Run Code Online (Sandbox Code Playgroud)
编辑:
我尝试使用object_getClass,如holex所提到的,但现在我得到:
错误:"类型'CityInfo.Type'不符合协议'AnyObject'"
需要做什么才能符合协议?
class CityInfo : NSObject {
var cityName: String?
var regionCode: String?
var regionName: String?
}
Run Code Online (Sandbox Code Playgroud)
Eli*_*eda 135
您正在以错误的方式接近它:在Swift中,与Objective-C不同,类具有特定类型,甚至具有继承层次结构(即,如果类B继承A,则B.Type也继承自A.Type):
class A {}
class B: A {}
class C {}
// B inherits from A
let object: A = B()
// B.Type also inherits from A.Type
let type: A.Type = B.self
// Error: 'C' is not a subtype of 'A'
let type2: A.Type = C.self
Run Code Online (Sandbox Code Playgroud)
这就是为什么你不应该使用AnyClass,除非你真的想允许任何课程.在这种情况下,正确的类型将是T.Type,因为它表示returningClass参数和闭包参数之间的链接.
实际上,使用它代替AnyClass允许编译器正确推断方法调用中的类型:
class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: T.Type, completionHandler handler: ((T) -> ())) {
// The compiler correctly infers that T is the class of the instances of returningClass
handler(returningClass())
}
Run Code Online (Sandbox Code Playgroud)
现在有一个构造T传递给的实例的问题handler:如果你现在尝试运行代码,编译器会抱怨它T是不可构造的().理所当然地:T必须明确约束,要求它实现特定的初始化程序.
这可以通过以下协议来完成:
protocol Initable {
init()
}
class CityInfo : NSObject, Initable {
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
}
Run Code Online (Sandbox Code Playgroud)
那么你只需要改变的泛型约束invokeService的<T>到<T: Initable>.
如果您遇到奇怪的错误,例如"无法将表达式的类型'()'转换为'String'"类型,则将方法调用的每个参数移动到其自己的变量通常很有用.它有助于缩小导致错误的代码并发现类型推断问题:
let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/
}
Run Code Online (Sandbox Code Playgroud)
现在有两种可能性:错误移动到其中一个变量(这意味着错误的部分存在)或者你得到一个神秘的消息,如"无法将表达式的类型转换()为类型($T6) -> ($T6) -> $T5".
后一个错误的原因是编译器无法推断您编写的类型.在这种情况下,问题是T仅在闭包的参数中使用,并且您传递的闭包不指示任何特定类型,因此编译器不知道要推断的类型.通过更改returningClass要包含的类型,T您可以为编译器提供一种确定泛型参数的方法.
hol*_*lex 34
你可以AnyObject通过这种方式获得课程:
let myClass: AnyClass = type(of: self)
Run Code Online (Sandbox Code Playgroud)
let myClass: AnyClass = object_getClass(self)
Run Code Online (Sandbox Code Playgroud)
如果你愿意,你可以稍后将它作为参数传递给你.
小智 9
我在 swift5 中有一个类似的用例:
class PlistUtils {
static let shared = PlistUtils()
// write data
func saveItem<T: Encodable>(url: URL, value: T) -> Bool{
let encoder = PropertyListEncoder()
do {
let data = try encoder.encode(value)
try data.write(to: url)
return true
}catch {
print("encode error: \(error)")
return false
}
}
// read data
func loadItem<T: Decodable>(url: URL, type: T.Type) -> Any?{
if let data = try? Data(contentsOf: url) {
let decoder = PropertyListDecoder()
do {
let result = try decoder.decode(type, from: data)
return result
}catch{
print("items decode failed ")
return nil
}
}
return nil
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
83876 次 |
| 最近记录: |