I just encountered a strange behavior in swift's inheritance handling, when it comes to polymorphism and dynamic types. The following code shows the problem I encounter, which basically is: The dynamic type is recognized correctly (printed by print("type(of: self) = \(classType)")
), but the generic function testGeneric
uses the wrong type.
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()
the printed output is
type(of: self) = TestClass
T.Type = TestSuperClass
type(of: self) = TestClass2
T.Type = TestClass2
So basically when calling testClass.run()
, type(of: self)
yields TestClass
, which I would expect. The problem then is that the generic function testGeneric
, which is called immediately afterwards, somehow does not work with type TestClass
, but uses TestSuperClass
instead.
What I personally would expect is
type(of: self) = TestClass
T.Type = TestClass
type(of: self) = TestClass2
T.Type = TestClass2
i.e., the generic function testGeneric
using the type TestClass
instead of TestSuperClass
when called via testClass.run()
.
Questions:
- Do you have an explanation for that?
- How can I obtain the behavior I had in mind?
In Swift, the compiler want's to know at compile time which generic type to "infer". Therefore, the type system will bind to the static type. There is no such thing as dynamic type inference.
Therefore the compiler generates the following (see comments):
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>
}
}
As a result, T.self
is TestSuperClass
in your case, because that's what the compiler is able to see:
static func testGeneric<T: TestSuperClass>(of type: T.Type) {
print("T.Type = \(T.self)")
}
What you maybe want is the following:
static func testGeneric<T: TestSuperClass>(of type: T.Type) {
print("T.Type = \(type)")
}
Here, you do not print the type of T
, but the (dynamic) value of the parameter type
, which in your case is TestClass