Вызов Generics с типом T в Swift
В моем приложении я хочу создать общий метод, который создает массив объекта, зависящего от заданного типа T.
Я создал следующую функцию:
func getArray<T : ROJSONObject>(key:String) -> T[] {
var elements = T[]()
for jsonValue in getValue(key).array! {
var element = T()
element.jsonData = jsonValue
elements.append(element)
}
return elements
}
Теперь я хочу передать тип при вызове метода, чтобы он знал, какой тип он должен создать внутри. Я думаю, что в Java и С# вы можете использовать такой метод:
object.getArray<Document>("key")
Когда я называю это так, я всегда получаю ошибку:
Cannot explicitly specialize a generic function
Итак, мое исправление заключалось в том, чтобы определить дополнительный параметр, содержащий экземпляр типа T, чтобы он автоматически обнаруживал тип:
func getArray<T : ROJSONObject>(key:String, type:T) -> T[] {
var elements = T[]()
for jsonValue in getValue(key).array! {
var element = T()
element.jsonData = jsonValue
elements.append(element)
}
return elements
}
Нет ли другого способа получить это поведение без передачи неиспользуемого экземпляра? Или я что-то не понимаю?
Дальнейшая проверка
После ответа jtbandes я сделал еще несколько тестов. Я попытался заставить Type, добавив as
в вызов.
class Person {
init() { }
func getWorkingHours() -> Float {
return 40.0
}
}
class Boss : Person {
override func getWorkingHours() -> Float {
println(100.0)
return 100.0
}
}
class Worker : Person {
override func getWorkingHours() -> Float {
println(42.0)
return 42.0
}
}
func getWorkingHours<T : Person>() -> T {
var person = T()
person.getWorkingHours()
return person
}
var worker:Worker = getWorkingHours() as Worker
var boss:Boss = getWorkingHours() as Boss
worker.getWorkingHours() // prints out 40.0 instead of 42.0
boss.getWorkingHours() // prints out 40.0 instead of 100.0
Итак, каким-то образом тип всегда является базовым типом, даже я указал тип с ключевым словом as
. Я знаю, что пример не имеет особого смысла, но он просто предназначен для тестирования.
Ответы
Ответ 1
Я думаю, что это ошибка.
Вы можете обойти это, сделав класс подклассом NSObject
или конструктором меток базового класса с @required
import Cocoa
class A : NSObject {
init() { }
}
class B : A {}
class C : A {}
func Create<T:NSObject> () -> T {
return T()
}
println(Create() as A)
println(Create() as B)
println(Create() as C)
//<_TtC11lldb_expr_01A: 0x7f85ab717bc0>
//<_TtC11lldb_expr_01B: 0x7f85ab451e00>
//<_TtC11lldb_expr_01C: 0x7f85ab509160>
class D {
@required init() { }
}
class E : D {
init() { }
}
class F : D {
init() { }
}
func Create2<T:D> () -> T {
return T()
}
println(Create2() as D)
println(Create2() as E)
println(Create2() as F)
//C11lldb_expr_01D (has 0 children)
//C11lldb_expr_01E (has 1 child)
//C11lldb_expr_01F (has 1 child)
Не знаю, почему @required
решить проблему. Но это ссылка
требуется
Применить этот атрибут к назначенному или удобному инициализатор класса, чтобы указать, что каждый подкласс должен реализовывать этот инициализатор.
Обязательные назначенные инициализаторы должны быть реализованы явно. Необходимые инициализаторы удобства могут быть либо реализованы явно или наследуется, когда подкласс непосредственно реализует все суперклассы обозначают инициализаторы (или когда подкласс перекрывается назначенные инициализаторы с инициализаторами удобства).
Ответ 2
Я работал вокруг этого, заимствуя из быстрой функции времени выполнения unsafeBitCast
.
Он объявлен как func unsafeBitCast<T, U>(x: T, _: U.Type) -> U
, и вы можете назвать его unsafeBitCast(value, MyType)
.
Применительно к вашей функции это будет
func getArray<T : ROJSONObject>(key:String, _: T.Type) -> T[] {
// function stays the same
}
И вы можете так называть
getArray("key", Document)
Ответ 3
Вы должны указать тип с ключевым словом as
: object.getArray("key") as Document[]
.
Ответ 4
Вам нужно сделать инициализатор required
, затем добавить строку let realType = T.self
и заменить T()
на realType()
.
class Person {
required init() { }
func getWorkingHours() -> Float {
return 40.0
}
}
class Boss : Person {
override func getWorkingHours() -> Float {
println(100.0)
return 100.0
}
}
class Worker : Person {
override func getWorkingHours() -> Float {
println(42.0)
return 42.0
}
}
func getWorkingHours<T : Person>() -> T {
let realType = T.self
var person = realType()
person.getWorkingHours()
return person
}
Ответ 5
Чтобы ответить на первую часть вашего вопроса
Вам не нужно добавлять дополнительный параметр к методу, чтобы указать тип T
, поскольку вы уже bat [T]
в качестве типа возврата
чтобы вы заменили эту строку
object.getArray<Document>("key")
с этим
object.getArray("key") as [Document]
Таким образом вы получите возможность указать тип T
.
Ответ 6
Не называйте это
object.getArray<Document>("key")
Вместо этого оставьте "<Document> " части, например:
object.getArray("key")
Исправлена проблема для меня.
Ответ 7
Рабочая версия кода в вопросе (вторая часть)
• Инициатор требуется, потому что функция возвращает общий тип лица ( newPerson(as person:T) -> T
)
• Я заменил методы класса func getWorkingHours()
на var workingHours { get }
.. Он работает одинаково, более "экономным" способом.
• getWorkingHours()
возвращает часы, как указывает имя, а не Person..
class Person {
required init() {}
var workingHours: Float { get { return 40 } }
}
class Boss : Person {
override var workingHours: Float { get { return 100 } }
}
class Worker : Person {
override var workingHours: Float { get { return 42 } }
}
func getWorkingHours<T : Person>(_ person: T) -> Float {
return person.workingHours
}
func newPerson<T : Person>(as person: T) -> T {
return T()
}
// -----------------------------------------------
let worker = Worker()
let boss = Boss()
print( getWorkingHours(worker) ) // prints 42.0
print( getWorkingHours(boss) ) // prints 100.0
let person = newPersonOfSameClass(as: worker)
print( getWorkingHours(person) ) // prints 42.0
Надеюсь, что это поможет. Этот код готов для копирования/вставки на игровой площадке.