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 preserve type other than the specified conditions of the generic e.g. conforming to a base class. It seems like all i can cast or initialize is the base class. What's even more strange is when i'm in the debugger the generic appears to have a RawPointer to the correct class and even the variables look like they're of the right type:
EDIT:
As of Xcode 6.1 this is still an issue (simplified code courtesy of Gregory Higley) :
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()
Still prints out "I am BaseClass"
Also would like to note that required init{} in the base class no longer works.
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?
DerivedClass()
as specified with T
.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()
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With