Swift generics not preserving type

前端 未结 4 570
借酒劲吻你
借酒劲吻你 2020-11-28 11:23

I\'m trying cast and/or generate a variable based upon the specified generic type. I understand there is no type erasure in swift, but it doesn\'t seem like the generics pre

相关标签:
4条回答
  • 2020-11-28 11:44

    This code works as expected.

    class BaseClass {
    
        required init() {} // <-- ADDED THIS
    
        func printme() -> Void {
            println("I am BaseClass")
        }
    }
    
    class DerivedClass : BaseClass {
        override func printme() -> Void {
            println("I am DerivedClass")
        }
    }
    
    class Util<T: BaseClass> {
        func doSomething() {
            var instance = T()
            instance.printme()
        }
    }
    
    var util = Util<DerivedClass>()
    util.doSomething()
    

    Code base are stolen from @GregoryHigley answer :)

    Marking init() {} as required did the thing. This guarantees init() is the designated initializer of ANY derived class from BaseClass.

    Without it, one can make illegal subclass like:

    class IllegalDerivedClass : BaseClass {
        var name:String
    
        init(name:String) {
            self.name = name
            super.init()
        }
    
        override func printme() -> Void {
            println("I am DerivedClass")
        }
    }
    
    var util = Util<IllegalDerivedClass>()
    util.doSomething()
    

    You know this doesn't work because IllegalDerivedClass doesn't inherit init() initializer.

    I think, that is the reason of your problem.

    Anyway, whose fault is that?

    • Compiler should warn about ambiguousness.
    • Runtime should try to initialize DerivedClass() as specified with T.
    • Debugger should show instance is a instance of BaseClass as it actually is.

    ADDED:

    As of Xcode 6.1 GM 2, It seems, you need more work. (in addition to required init() {})

    class Util<T: BaseClass> {
        let theClass = T.self // store type itself to variable
    
        func doSomething() {
            var instance = theClass() // then initialize
            instance.printme()
        }
    }
    

    I have absolutely no idea why we need this, what's going on X(

    ADDED:2014/10/18

    I found this also works:

        func doSomething() {
            var instance = (T.self as T.Type)()
            instance.printme()
        }
    

    ADDED: 2015/02/10

    As of Xcode Version 6.3 (6D520o) / Swift 1.2

    We no longer need (T.self as T.Type)() hack. Just T() works as long as T has required init() initializer.

    class Util<T: BaseClass> {
        func doSomething() {
            var instance = T()
            instance.printme()
        }
    }
    
    0 讨论(0)
  • 2020-11-28 11:46

    The problem is var instance = T() Initialisers are not virtual so the instance is always made with BaseClass()*. The following code uses a class function to work around the problem:

    class BaseClass {
        func printme() -> String {
            return "I am BaseClass"
        }
        class func makeInstance() -> BaseClass
        {
            return BaseClass()
        }
    }
    
    class DerivedClass : BaseClass {
        override class func makeInstance() -> BaseClass
        {
            return DerivedClass()
        }
    
        override func printme() -> String {
            return "I am DerivedClass"
        }
    }
    
    class Util<T: BaseClass> {
        func doSomething() -> String {
            var instance = T.makeInstance()
            return instance.printme()
        }
    }
    
    var util = Util<DerivedClass>()
    println("\(util.doSomething())")
    

    I changed the implementation of printme() only because the original code didn't print anything in a playground for some reason.

    * I think this is still a bug.

    0 讨论(0)
  • 2020-11-28 11:53

    I had a similar issue. You need to add required initializer and let realType = T.self and replace T() with realType().

    class BaseClass {
        required init() {}
        func printme() -> Void {
            println("I am BaseClass")
        }
    }
    
    class DerivedClass : BaseClass {
        override func printme() -> Void {
            println("I am DerivedClass")
        }
    }
    
    class Util<T: BaseClass> {
        func doSomething() {
            let realType = T.self // that's it
            var instance = realType()
            instance.printme()
        }
    }
    
    var util = Util<DerivedClass>()
    util.doSomething()
    
    0 讨论(0)
  • 2020-11-28 12:00

    I created a simplified version of your code as follows:

    class BaseClass {
        func printme() -> Void {
            println("I am BaseClass")
        }
    }
    
    class DerivedClass : BaseClass {
        override func printme() -> Void {
            println("I am DerivedClass")
        }
    }
    
    class Util<T: BaseClass> {
        func doSomething() {
            var instance = T()
            instance.printme()
        }
    }
    
    var util = Util<DerivedClass>()
    util.doSomething()
    

    This distills the problem to its essence. One would expect util.doSomething() to print "I am DerivedClass", but it prints "I am BaseClass" every time. This has to be a bug, because no rational type system would work in this way.

    I think you should file this with Apple as a bug.

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