Невозможно явно выделить обобщенную функцию
У меня проблема со следующим кодом:
func generic1<T>(name : String){
}
func generic2<T>(name : String){
generic1<T>(name)
}
результат generic1 (name) для ошибки компилятора "Не может явно специализировать общую функцию"
Можно ли избежать этой ошибки? Я не могу изменить подпись функции generic1, поэтому она должна быть (String) → Void
Ответы
Ответ 1
У меня также была эта проблема, и я нашел обходное решение для моего дела.
В этой статье автор имеет ту же проблему
https://www.iphonelife.com/blog/31369/swift-programming-101-generics-practical-guide
Таким образом, проблема заключается в том, что компилятор должен каким-то образом вывести тип T. Но не допускается просто использовать общий <type> (params...).
Обычно компилятор может искать тип T, просматривая типы параметров, потому что во многих случаях используется T.
В моем случае это было немного иначе, потому что возвращаемый тип моей функции был T. В вашем случае кажется, что вы вообще не использовали T в своей функции. Я думаю, вы просто упростили код примера.
Итак, у меня есть следующая функция
func getProperty<T>( propertyID : String ) -> T
И в случае, например,
getProperty<Int>("countProperty")
компилятор дает мне
Cannot explicitly specialize a generic function
Итак, чтобы дать компилятору другой источник информации, чтобы вывести тип T, вы должны явно объявить тип переменной, в которой сохраняется возвращаемое значение.
var value : Int = getProperty("countProperty")
Таким образом, компилятор знает, что T должен быть целым числом.
Итак, я думаю, что в целом это просто означает, что если вы укажете общую функцию, вам нужно хотя бы использовать T в ваших типах параметров или в качестве типа возврата.
Ответ 2
Решение принимает тип класса как параметр (например, в Java)
Чтобы компилятор знал, какой тип он имеет дело с передачей класса в качестве аргумента
extension UIViewController {
func navigate<ControllerType: UIViewController>(_ dump: ControllerType.Type, id: String, before: ((ControllerType) -> Void)?){
let controller = self.storyboard?.instantiateViewController(withIdentifier: id) as! ControllerType
before?(controller)
self.navigationController?.pushViewController(controller, animated: true)
}
}
Вызов как:
self.navigate(UserDetailsViewController.self, id: "UserDetailsViewController", before: {
controller in
controller.user = self.notification.sender
})
Ответ 3
Swift 4.2
Как правило, существует множество способов определения общих функций. Но они основаны на условии, что T
должен использоваться как parameter
или как return type
.
extension UIViewController {
class func doSomething<T: UIView>() -> T {
return T()
}
class func doSomethingElse<T: UIView>(value: T) {
// Note: value is a instance of T
}
class func doLastThing<T: UIView>(value: T.Type) {
// Note: value is a MetaType of T
}
}
После этого мы должны предоставить T
при звонке.
let result = UIViewController.doSomething() as UIImageView // Define 'T' by casting, as UIImageView
let result: UILabel = UIViewController.doSomething() // Define 'T' with property type, as UILabel
UIViewController.doSomethingElse(value: UIButton()) // Define 'T' with parameter type, as UIButton
UIViewController.doLastThing(value: UITextView.self) // Define 'T' with parameter type, as UITextView
Ref:
- http://austinzheng.com/2015/01/02/swift-generics-pt-1/
- https://dispatchswift.com/type-constraints-for-generics-in-swift-d6bf2f0dbbb2
Ответ 4
Здесь вам не требуется общий тип, поскольку у вас есть статические типы (параметр String as), но если вы хотите, чтобы общая функция вызывала другую, вы могли бы сделать следующее.
Использование общих методов
func fetchObjectOrCreate<T: NSManagedObject>(type: T.Type) -> T {
if let existing = fetchExisting(type) {
return existing
}
else {
return createNew(type)
}
}
func fetchExisting<T: NSManagedObject>(type: T.Type) -> T {
let entityName = NSStringFromClass(type)
// Run query for entiry
}
func createNew<T: NSManagedObject>(type: T.Type) -> T {
let entityName = NSStringFromClass(type)
// create entity with name
}
Использование универсального класса (Менее гибкий как общий может быть определен для 1 типа только для каждого экземпляра)
class Foo<T> {
func doStuff(text: String) -> T {
return doOtherStuff(text)
}
func doOtherStuff(text: String) -> T {
}
}
let foo = Foo<Int>()
foo.doStuff("text")
Ответ 5
Я думаю, что когда вы укажете общую функцию, вы должны указать некоторые параметры типа T, например:
func generic1<T>(parameter: T) {
println("OK")
}
func generic2<T>(parameter: T) {
generic1(parameter)
}
и если вы хотите вызвать метод handle(), вы можете сделать это, написав протокол и указав ограничение типа для T:
protocol Example {
func handle() -> String
}
extension String: Example {
func handle() -> String {
return "OK"
}
}
func generic1<T: Example>(parameter: T) {
println(parameter.handle())
}
func generic2<T: Example>(parameter: T) {
generic1(parameter)
}
чтобы вы могли вызвать эту общую функцию со String:
generic2("Some")
и он скомпилирует
Ответ 6
У меня была аналогичная проблема с моей общей функцией класса class func retrieveByKey<T: GrandLite>(key: String) -> T?
.
Я не мог назвать его let a = retrieveByKey<Categories>(key: "abc")
, где Категории являются подклассом GrandLite.
let a = Categories.retrieveByKey(key:"abc")
вернулся GrandLite, а не категории. Общие функции не вызывают тип, основанный на классе, который их вызывает.
class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T?
дал мне ошибку, когда я попробовал let a = Categories.retrieveByKey(aType: Categories, key: "abc")
дал мне ошибку, что он не смог преобразовать категории .Type в GrandLite, хотя категории - это подкласс GrandLite. ОДНАКО...
class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T?
работал, если я пробовал let a = Categories.retrieveByKey(aType: [Categories](), key: "abc")
, очевидно, что явное назначение подкласса не работает, но неявное присвоение с использованием другого родового типа (массива) работает в Swift 3.
Ответ 7
class UploadResult: Parsable{
...
}
func upload<T:Parsable>(type: T.Type, image: UIImage,
success:@escaping (_ error:ApiError?, _ result:T?)->Void )
{...}
UploadApi.upload(type: UploadResult.self, image: imageForUpload.image!) {...}