【问题标题】:Problem with swift polymorphism and dynamic type快速多态性和动态类型的问题
【发布时间】:2019-05-20 00:43:55
【问题描述】:

当涉及到多态性和动态类型时,我刚刚在 swift 的继承处理中遇到了一个奇怪的行为。下面的代码显示了我遇到的问题,基本上是:动态类型被正确识别(print("type(of: self) = \(classType)")打印),但是泛型函数testGeneric使用了错误的类型。

class Global {
    static func testGeneric<T: TestSuperClass>(of type: T.Type) {
        print("T.Type = \(T.self)")
    }
}

class TestSuperClass {
    func run() {
        let classType = type(of: self)
        print("type(of: self) = \(classType)")
        Global.testGeneric(of: classType)
    }
}

class TestClass: TestSuperClass {

}

class TestClass2: TestSuperClass {
    override func run() {
        let classType = type(of: self)
        print("type(of: self) = \(classType)")
        Global.testGeneric(of: classType)
    }
}

let testClass = TestClass()
let testClass2 = TestClass2()

testClass.run()
testClass2.run()

打印输出是

type(of: self) = TestClass
T.Type = TestSuperClass
type(of: self) = TestClass2
T.Type = TestClass2

所以基本上在调用testClass.run() 时,type(of: self) 会产生TestClass,这是我所期望的。问题在于,随后立即调用的通用函数testGeneric 不知何故不适用于TestClass,而是使用TestSuperClass

我个人的期望是

type(of: self) = TestClass
T.Type = TestClass
type(of: self) = TestClass2
T.Type = TestClass2

即,通用函数 testGeneric 在通过 testClass.run() 调用时使用类型 TestClass 而不是 TestSuperClass

问题:
- 你对此有什么解释吗?
- 我怎样才能得到我想到的行为?

【问题讨论】:

    标签: swift inheritance dynamic polymorphism dynamictype


    【解决方案1】:

    在 Swift 中,编译器希望在编译时知道要“推断”哪个泛型类型。因此,类型系统将绑定到静态类型。没有动态类型推断这样的东西。

    因此编译器会生成以下内容(参见 cmets):

    class TestSuperClass {
        func run() {
            let classType = type(of: self)  // static MetaType TestSuperClass.Type
            print("type(of: self) = \(classType)") // dynamic type: TestClass
            Global.testGeneric(of: classType)  // infer to static type, i.e. testGeneric<TestSuperClass>
        }
    }
    

    因此,在您的情况下,T.selfTestSuperClass,因为这是编译器能够看到的:

    static func testGeneric<T: TestSuperClass>(of type: T.Type) {
        print("T.Type = \(T.self)")
    }
    

    您可能想要的是以下内容:

    static func testGeneric<T: TestSuperClass>(of type: T.Type) {
        print("T.Type = \(type)")
    }
    

    在这里,您不打印T 的类型,而是参数type 的(动态)值,在您的情况下为TestClass

    【讨论】:

    • 你知道如何处理像static func returnObjects&lt;T: TestSuperClass&gt;(of type: T.Type) -&gt; [T] 这样的通用函数吗? (代码如上,只需用 returnObjects 切换 testGeneric)。所以基本上T 应该根据动态类型而有所不同,但是根据您上面的解释,它不会,我的测试确实证实了这一点。谢谢!
    【解决方案2】:

    回答第二个问题:您将无法更改返回数组的动态类型;它永远是[TestSuperClass]——虽然它会包含TestClass对象:

    class Global {
        static func testGeneric<T: TestSuperClass>(of type: T.Type) {
            print("T.Type = \(T.self)")
        }
        static func returnObjects<T: TestSuperClass>(of theType: T.Type) -> [T] {
            let newObj = theType.init()
            let newObjType = type(of:newObj)
            print("type(of: newObj) = \(newObjType)")
            return [newObj]
        }
    }
    
    class TestSuperClass {
        required init() {
            print ("TestSuperClass.init")
        }
    
        func run() {
            let classType = type(of: self)
            print("type(of: self) = \(classType)")
            Global.testGeneric(of: classType)
            let array = Global.returnObjects(of: classType)
            let arrayType = type(of:array)
            print("type(of: self) = \(arrayType)")
    
            print (array)
        }
    }
    
    class TestClass: TestSuperClass {
        required init() {
            super.init()
            print("TestClass.init")
        }
    
    }
    
    let testClass = TestClass()
    testClass.run()
    

    TestSuperClass.init
    测试类.init
    type(of: self) = TestClass
    T.Type = TestSuperClass
    TestSuperClass.init
    测试类.init
    type(of: newObj) = TestClass
    type(of: self) = Array
    [__lldb_expr_21.TestClass]

    【讨论】:

    • 谢谢,这让我对如何处理手头的任务有了一些想法。
    猜你喜欢
    • 1970-01-01
    • 2013-09-14
    • 1970-01-01
    • 2011-08-29
    • 2021-11-14
    • 2020-12-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-06
    相关资源
    最近更新 更多