如何将类类型作为函数参数传递 [英] How to pass a class type as a function parameter
问题描述
我有一个通用函数调用Web服务并将JSON响应序列化回一个对象。
class func invokeService< ; T>(service:String,withParams params:Dictionary< String,String> ;, returningClass:AnyClass,completionHandler handler:((T) - >())){
/ *构造URL ,调用服务并解析响应* /
}
我试图完成就是相当于这个Java代码
public< T> T invokeService(final String serviceURLSuffix,final Map< String,String> params,
final Class< T> classTypeToReturn){
}
- 我的方法签名是否正确?
- 更具体地说, code> AnyClass 作为参数类型
正确的做法 - 调用该方法时,我将
MyObject.self
作为returningClass值,但我得到一个编译错误无法将表达式的类型'()'转换为键入'String'
CastDAO.invokeService(test,withParams:[test :test],returningClass:CityInfo.self){cityInfo in /*...*/
}
编辑:
code> object_getClass ,就像holex提到的那样,但现在我得到了:
错误:需要做什么才能遵守协议?
class CityInfo:NSObject {
var cityName :字符串?
var regionCode:String?
var regionName:String?
您正在接近错误的方式:在Swift中,与Objective-C不同,类有特定的类型,甚至有一个继承层次结构(也就是说,如果类 B
从继承,然后
B.Type
也继承自 A.Type
):
class A {}
class B:A {}
class C {}
// B继承自A
let object:A = B()
// B.Type也继承自A.Type
let type:A.Type = B. self
//错误:'C'不是'A'的子类型
let type2:A.Type = C.self
这就是为什么你不应该使用 AnyClass
,除非你真的想允许任何类。在这种情况下,正确的类型将是 T.Type
,因为它表示 returningClass
参数与参数实际上,使用它而不是 AnyClass
可以让编译器正确地推断出这些类型的类型。
方法调用:
$ b
class func invokeService< T>(service:String,withParams params:Dictionary< String,String> ;, returningClass: T.Type,completionHandler处理函数:((T) - >())){
//编译器正确地推断出T是returnedClass
handler(returningClass())$的实例的类
现在有一个构造 T
传递给处理程序
:如果您现在尝试运行代码,编译器会抱怨 T
不能用()
构造。正确的是: T
必须被明确地约束以要求它实现一个特定的初始化器。
这可以是使用如下协议完成:
protocol Initable {
init()
}
class CityInfo:NSObject,Initable {
var cityName:String?
var regionCode:String?
var regionName:String?
//在这里没什么改变,CityInfo已经实现了init()
}
然后,您只需将 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 / * ... * /
}
现在有两种可能性:错误移动到其中一个变量(这意味着错误的部分在那里),或者你得到一个神秘的信息,比如不能将表达式的类型()
转换为键入。
编译器无法推断出你写的东西的类型。在这种情况下,问题在于 T
仅用于闭包的参数,并且您传递的闭包并不表示任何特定的类型,因此编译器不知道什么类型来推断。通过将 returningClass
的类型更改为包含 T
,您可以为编译器提供一种确定泛型参数的方法。
I have a generic function that calls a web service and serialize the JSON response back to an object.
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 */
}
What I'm trying to accomplish is is the equivalent of this Java code
public <T> T invokeService(final String serviceURLSuffix, final Map<String, String> params,
final Class<T> classTypeToReturn) {
}
- Is my method signature for what I'm trying to accomplish correct?
- More specifically, is specifying
AnyClass
as a parameter type the
right thing to do?
- When calling the method, I'm passing
MyObject.self
as the returningClass value, but I get a compilation error "Cannot convert the expression's type '()' to type 'String'"
CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) { cityInfo in /*...*/
}
Edit:
I tried using object_getClass
, as mentioned by holex, but now I get:
error: "Type 'CityInfo.Type' does not conform to protocol 'AnyObject'"
What need to be done to conform to the protocol?
class CityInfo : NSObject {
var cityName: String?
var regionCode: String?
var regionName: String?
}
解决方案 You are approaching it in the wrong way: in Swift, unlike Objective-C, classes have specific types and even have an inheritance hierarchy (that is, if class B
inherits from A
, then B.Type
also inherits from 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
That's why you shouldn't use AnyClass
, unless you really want to allow any class. In this case the right type would be T.Type
, because it expresses the link between the returningClass
parameter and the parameter of the closure.
In fact, using it instead of AnyClass
allows the compiler to correctly infer the types in the method call:
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())
}
Now there's the problem of constructing an instance of T
to pass to handler
: if you try and run the code right now the compiler will complain that T
is not constructible with ()
. And rightfully so: T
has to be explicitly constrained to require that it implements a specific initializer.
This can be done with a protocol like the following one:
protocol Initable {
init()
}
class CityInfo : NSObject, Initable {
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
}
Then you only have to change the generic constraints of invokeService
from <T>
to <T: Initable>
.
Tip
If you get strange errors like "Cannot convert the expression's type '()' to type 'String'", it is often useful to move every argument of the method call to its own variable. It helps narrowing down the code that is causing the error and uncovering type inference issues:
let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/
}
Now there are two possibilities: the error moves to one of the variables (which means that the wrong part is there) or you get a cryptic message like "Cannot convert the expression's type ()
to type ($T6) -> ($T6) -> $T5
".
The cause of the latter error is that the compiler is not able to infer the types of what you wrote. In this case the problem is that T
is only used in the parameter of the closure and the closure you passed doesn't indicate any particular type so the compiler doesn't know what type to infer. By changing the type of returningClass
to include T
you give the compiler a way to determine the generic parameter.
这篇关于如何将类类型作为函数参数传递的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!