Anda mendekatinya dengan cara yang salah: di Swift, tidak seperti Objective-C, kelas memiliki tipe tertentu dan bahkan memiliki hierarki warisan (yaitu, jika kelas B
mewarisi dari A
, maka B.Type
juga mewarisi dari 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
Itu sebabnya Anda tidak boleh menggunakan AnyClass
, kecuali Anda benar-benar ingin mengizinkan kelas apa pun . Dalam hal ini tipe yang tepat adalah T.Type
, karena itu menyatakan hubungan antara returningClass
parameter dan parameter penutupan.
Bahkan, menggunakannya bukan AnyClass
memungkinkan kompiler untuk menyimpulkan dengan benar jenis-jenis dalam pemanggilan metode:
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())
}
Sekarang ada masalah membangun instance T
untuk dilewatkan handler
: jika Anda mencoba dan menjalankan kode sekarang kompiler akan mengeluh bahwa T
itu tidak dapat dibangun dengan ()
. Dan memang seharusnya begitu: T
harus dibatasi secara eksplisit untuk mengharuskannya mengimplementasikan inisialisasi tertentu.
Ini dapat dilakukan dengan protokol seperti berikut:
protocol Initable {
init()
}
class CityInfo : NSObject, Initable {
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
}
Maka Anda hanya perlu mengubah batasan umum dari invokeService
dari <T>
menjadi <T: Initable>
.
Tip
Jika Anda mendapatkan kesalahan aneh seperti "Tidak dapat mengonversi tipe ekspresi '()' menjadi tipe 'String'", sering kali berguna untuk memindahkan setiap argumen pemanggilan metode ke variabelnya sendiri. Ini membantu mempersempit kode yang menyebabkan kesalahan dan mengungkap masalah jenis inferensi:
let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/
}
Sekarang ada dua kemungkinan: kesalahan berpindah ke salah satu variabel (yang berarti ada bagian yang salah di sana) atau Anda mendapatkan pesan samar seperti "Tidak dapat mengonversi jenis ekspresi ()
ke jenis ($T6) -> ($T6) -> $T5
".
Penyebab kesalahan terakhir adalah bahwa kompiler tidak dapat menyimpulkan jenis dari apa yang Anda tulis. Dalam hal ini masalahnya adalah yang T
hanya digunakan dalam parameter penutupan dan penutupan yang Anda lewati tidak menunjukkan tipe tertentu sehingga kompiler tidak tahu tipe apa yang harus disimpulkan. Dengan mengubah tipe returningClass
untuk menyertakan T
Anda memberi kompiler cara untuk menentukan parameter generik.
CastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }