问题
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?
回答1:
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.
回答2:
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
回答3:
Update, i used Calculator as return type which makes no sense. It should be Self, but still a required init has to exist:
class Calculator {
func showKind() { println("regular") }
required init() {
}
}
class ScientificCalculator: Calculator {
let model: String = "HP-15C"
override func showKind() { println("scientific") }
}
extension Calculator {
class func create() -> Self {
return self()
}
}
var b = ScientificCalculator.create() // HP-15C
b.showKind()
I addition to Sean Woodward, it will work if Calculators have (required) initializers. Overriding that in a subclass is not needed.
class Calculator {
required init() {
}
func showKind() { println("regular") }
}
class ScientificCalculator: Calculator {
let model: String = "HP-15C"
override func showKind() { println("scientific") }
}
extension Calculator {
class func create<T:Calculator>( type:T.Type ) -> T {
return type() as T
}
class func newInstance() -> Calculator {
return self()
}
}
var b = ScientificCalculator.newInstance() // HP-15C
b.showKind() // sientific
回答4:
This doesn't explain why your generic approach isn't working. I'm just looking for an alternative.
It seems with Swift that they have replaced factory methods with convenience initializers. You can create a new convenience initializer and add that with an extension to Calculator
.
extension Calculator {
convenience init(name: String) {
println(name)
self.init()
}
}
let reg = Calculator(name: "Reg")
reg.showKind() // "regular"
let sci = ScientificCalculator(name: "Sci")
sci.showKind() // "scientific"
来源:https://stackoverflow.com/questions/27336607/is-it-possible-to-restrict-a-swift-generic-class-function-return-type-to-the-sam