Cannot explicitly specialize a generic function

后端 未结 7 2175
时光取名叫无心
时光取名叫无心 2020-12-04 11:06

I have issue with following code:

func generic1(name : String){
}

func generic2(name : String){
     generic1(name)
}


        
相关标签:
7条回答
  • 2020-12-04 11:24

    I had a similar problem with my generic class function class func retrieveByKey<T: GrandLite>(key: String) -> T?.

    I could not call it let a = retrieveByKey<Categories>(key: "abc") where Categories is a subclass of GrandLite.

    let a = Categories.retrieveByKey(key:"abc") returned GrandLite, not Categories. Generic functions do not infer type based on the class that calls them.

    class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T? gave me an error when I tried let a = Categories.retrieveByKey(aType: Categories, key: "abc") gave me an error that it could not convert Categories.Type to GrandLite, even though Categories is a subclass of GrandLite. HOWEVER...

    class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T? did work if I tried let a = Categories.retrieveByKey(aType: [Categories](), key: "abc") apparently an explicit assignment of a subclass does not work, but an implicit assigment using another generic type (array) does work in Swift 3.

    0 讨论(0)
  • 2020-12-04 11:25

    You don't need a generic here since you have static types (String as parameter), but if you want to have a generic function call another you could do the following.

    Using Generic methods

    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
    } 
    

    Using a generic class (Less flexible as the generic can be defined for 1 type only per instance)

    class Foo<T> {
    
       func doStuff(text: String) -> T {
          return doOtherStuff(text)
       }
    
       func doOtherStuff(text: String) -> T {
    
       }  
    
    }
    
    let foo = Foo<Int>()
    foo.doStuff("text")
    
    0 讨论(0)
  • 2020-12-04 11:32

    So far, my personal best practise used @orkhan-alikhanov 's answer. Today, when looking at SwiftUI and how .modifier() and the ViewModifier is implemented, I found another way (or is it more a workaround?)

    Simply wrap the second function into a struct.

    Example:

    If this one gives you the "Cannot explicitly specialize a generic function"

    func generic2<T>(name: String){
         generic1<T>(name)
    }
    

    This one might help. Wrap the declaration of generic1 into a struct:

    struct Generic1Struct<T> {
        func generic1(name: String) {## do, whatever it needs with T ##}
    }
    

    and call it with:

    func generic2<T>(name : String){
         Generic1Struct<T>().generic1(name: name)
    }
    

    Remarks:

    • I don't know if it helps in any possible case, when this error message occurs. I just know, that I was stuck many times when this came up. I do know, that this solution helped today, when the error-message came up.
    • The way Swift handles Generics is for me still confusing.
    • This example and the workaround with the struct is a good example. The workaround here has not a bit more information - but passes the compiler. Same information, but different results? Then something is wrong. If it is a compiler-bug it can get fixed.
    0 讨论(0)
  • 2020-12-04 11:40

    Swift 5

    Typically there are many ways to define generic functions. But they are based on condition that T must be used as a parameter, or as a 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
        }
    }
    

    After that, we must provide T when calling.

    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:

    1. http://austinzheng.com/2015/01/02/swift-generics-pt-1/
    2. https://dispatchswift.com/type-constraints-for-generics-in-swift-d6bf2f0dbbb2
    0 讨论(0)
  • 2020-12-04 11:41

    The solution is taking the class type as parameter (like in Java)

    To let compiler know what type he is dealing with pass the class as argument

    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)
        }
    }
    

    Call as:

    self.navigate(UserDetailsViewController.self, id: "UserDetailsViewController", before: {
            controller in
            controller.user = self.notification.sender
        })
    
    0 讨论(0)
  • 2020-12-04 11:42

    I think that when you specify generic function you should specify some of the parameters of type T, like followed:

    func generic1<T>(parameter: T) {
        println("OK")
    }
    
    func generic2<T>(parameter: T) {
        generic1(parameter)
    }
    

    and if you want to call handle() method, then you may do this by writing protocol, and specifying type constraint for 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)
    }
    

    so you may call this generic function with String:

    generic2("Some")
    

    and it will compile

    0 讨论(0)
提交回复
热议问题