I am extending a base class (one which I do not control) in Swift. I want to provide a class function for creating an instance typed to a subclass. A generic function is required. However, an implementation like the one below does not return the expected subclass type.
class Calculator {
func showKind() { println("regular") }
}
class ScientificCalculator: Calculator {
let model: String = "HP-15C"
override func showKind() { println("scientific") }
}
extension Calculator {
class func create<T:Calculator>() -> T {
let instance = T()
return instance
}
}
let sci: ScientificCalculator = ScientificCalculator.create()
sci.showKind()
The debugger reports T
as ScientificCalculator
, but sci
is Calculator
and calling sci.showKind()
returns "regular".
Is there a way to achieve the desired result using generics, or is it a bug?
Ok, from the developer forums, if you have control of the base class, you might be able to implement the following work around.
class Calculator {
func showKind() { println("regular") }
required init() {}
}
class ScientificCalculator: Calculator {
let model: String = "HP-15C"
override func showKind() { println("\(model) - Scientific") }
required init() {
super.init()
}
}
extension Calculator {
class func create<T:Calculator>() -> T {
let klass: T.Type = T.self
return klass()
}
}
let sci:ScientificCalculator = ScientificCalculator.create()
sci.showKind()
Unfortunately if you do not have control of the base class, this approach is not possible.
There is a bug somewhere - I mean, not in your code :)
This is obviously a generic method:
class func create<T:Calculator>() -> T
T
is inferred by the type of the variable the return value is assigned to, which in your case is ScientificCalculator
.
A more interesting case. Let's modify the create
function such that the type of T
is made explicit, regardless of the type of the variable the instance is assigned to:
class func create<T:Calculator>(type: T.Type) -> T
let sci: ScientificCalculator = ScientificCalculator.create(ScientificCalculator.self)
The result is the same.
A more interesting observation: sci
is a variable of ScientificCalculator
type, but it points to an instance of Calculator
. So this code:
sci.model
compiles, but generates a runtime exception - because there's no model
property defined in the base class.
This is clearly a compiler bug: an instance of a superclass is assigned to a variable whose type is one of its subclasses - that should never be possible (although the opposite is possible)
Also read my answer to a similar question
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