Swift generics not preserving type
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?
- Compiler should warn about ambiguousness.
- Runtime should try to initialize
DerivedClass()
as specified withT
. - Debugger should show
instance
is a instance ofBaseClass
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()
}
}
Generic type not preserved when called within another generic function
You're trying to reinvent class inheritance with generics. That is not what generics are for, and they don't work that way. Generic methods are statically dispatched, which means that the code is chosen at compile-time, not runtime. An overload should never change the behavior of the function (which is what you're trying to do here). Overrides in where
clauses can be used to improve performance, but they cannot be used to create dynamic (runtime) dispatch.
If you must use inheritance, then you must use classes. That said, the problem you've described is better solved with a generic Task rather than a protocol. For example:
struct Task<Result> {
let execute: () throws -> Result
}
enum TaskRunner {
static func run<Result>(task: Task<Result>) throws -> Result {
try task.execute()
}
}
let specificTask = Task(execute: { "Some Result" })
print(try TaskRunner.run(task: specificTask)) // Prints "Some Result"
Notice how this eliminates the "task not supported" case. Rather than being a runtime error, it is now a compile-time error. You can no longer call this incorrectly, so you don't have to check for that case.
If you really want dynamic dispatch, it is possible, but you must implement it as dynamic dispatch, not overloads.
enum TaskRunner<T: Task> {
static func run(task: T) throws -> T.Result {
switch task {
case is SpecificTask:
// execute a SpecificTask
return "Some Result" as! T.Result // <=== This is very fragile
default:
throw SomeError.error
}
}
}
This is fragile because of the as! T.Result
. If you change the result type of SpecificTask to something other than String, it'll crash. But the important point is the case is SpecificTask
, which is determined at runtime (dynamic dispatch). If you need task
, and I assume you do, you'd swap that with if let task = task as? SpecificTask
.
Before going down that road, I'd reconsider the design and see how this will really be called. Since the Result type is generic, you can't call arbitrary Tasks in a loop (since all the return values have to match). So it makes me wonder what kind of code can actually call run
.
swift subclasses used in generics don't get called when inheriting from NSObject
I was able to confirm your results and submitted it as a bug, https://bugs.swift.org/browse/SR-10617. Turns out this is a known issue! I was informed (by good old Hamish) that I was duplicating https://bugs.swift.org/browse/SR-10285.
In my bug submission, I created a clean compact reduction of your example, suitable for sending to Apple:
protocol P {
init()
func doThing()
}
class Wrapper<T:P> {
func go() {
T().doThing()
}
}
class A : NSObject, P {
required override init() {}
func doThing() {
print("A")
}
}
class B : A {
required override init() {}
override func doThing() {
print("B")
}
}
Wrapper<B>().go()
On Xcode 9.2, we get "B". On Xcode 10.2, we get "A". That alone is enough to warrant a bug report.
In my report I listed three ways to work around the issue, all of which confirm that this is a bug (because none of them should make any difference):
make the generic parameterized type's constraint be A instead of P
or, mark the protocol P as
@objc
or, don't have A inherit from NSObject
UPDATE: And it turns out (from Apple's own release notes) there's yet another way:
- mark A's
init
as@nonobjc
How to store generic type to avoid internal struct requiring it in Swift?
Here is possible approach (however I'd keep it outside, like ButtonStyle
is outside of Button
)... anyway, here it is:
public struct Example {
private let content: AnyView
init<Content: View>(@ViewBuilder content: @escaping () -> Content) {
self.content = AnyView(content())
}
func contentView() -> some View {
self.content
}
public struct ActionKey: Hashable, Equatable, RawRepresentable {
public static let cancelButtonClicked = ActionKey("cancelButtonClicked") // Static stored properties not supported in generic types
public static func == (lhs: ActionKey, rhs: ActionKey) -> Bool {
return lhs.rawValue == rhs.rawValue
}
public let rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
public init(_ rawValue: String) {
self.init(rawValue: rawValue)
}
}
}
Swift generics with variables
You need to declare the readings
params as [T]
.
class GeneralUpdate<T: Reading> {
var readingType: ReadingType
var dataSource: DataSource
var readings: [T]
init(readingType: ReadingType, dataSource: DataSource, readings: [T]) {
self.readingType = readingType
self.dataSource = dataSource
self.readings = readings
}
}
And please get rid of all that ugly
!
Implicitly Unwrapped Optionals.
How to properly use generics in Swift to store different types of objects in a list in Realm?
This is not going to work
let ChildObjects = List<O>() //...two possible types: subjects & mistakes
Realm Collections (Result, List) are homogenous and can only store zero or more instances of one object type; e.g. you can't store different classes of Object in the same List object
Generics are great but when you're working with different (possibly unrelated) objects, subjects and mistakes, using a generic is probably the wrong approach because queries and the actually data will be quite different.
Keep your data separate
let subjects = List<SubjectClass>()
and
let mistakes = List<MistakeClass>()
of if there is some connection make it one class
class SubjectClass: Object {
@objc dynamic var subject_name = ""
@objc dynamic var is_mistake = false
}
of if a subject can have lots of mistakes
class SubjectClass: Object {
@objc dynamic var subject_name = ""
let allMyMistakes = List<MistakeClass<>
}
For some data modeling info, review this answer and then a fantasic answer from @DávidPásztor here
How do you store Swift object with generic properties in Core Data?
Using generics and Core Data together is not easy so I think that in this case where you have only two parameter type, Int and Double, that the easiest solution is to create an NSManagedObject subclass for each. It's not the most optimal solution but it is quite straightforward and easy to work with. You will of course need to duplicate your fetch request etc but on the other hand you won't need to do any complicated casting to the correct generic type.
Here is how the Double
variant could look
class CDParameterProfileDouble: NSManagedObject {
@NSManaged var parameterName: String
@NSManaged var lowerBound: Double
@NSManaged var upperBound: Double
@NSManaged var units: String
@NSManaged var controlStep: Double
func toDomain() -> ParameterProfile<Double> {
ParameterProfile(parameterName: self.parameterName,
bounds: self.lowerBound...self.upperBound,
units: self.units,
controlStep: self.controlStep)
}
}
and the same for Int
class CDParameterProfileInt: NSManagedObject {
@NSManaged var parameterName: String
@NSManaged var lowerBound: Int
@NSManaged var upperBound: Int
@NSManaged var units: String
@NSManaged var controlStep: Int
func toDomain() -> ParameterProfile<Int> {
ParameterProfile(parameterName: self.parameterName,
bounds: self.lowerBound...self.upperBound,
units: self.units,
controlStep: self.controlStep)
}
}
Related Topics
Swift Random Float Between 0 and 1
How to Unwrap Double Optionals
How to Hash Nsstring With Sha1 in Swift
How to Unwrap an Optional Value from Any Type
How to Convert a View (Not Uiview) to an Image
Swift 2 - Pattern Matching in "If"
How to Make a Random Color With Swift
When Should I Access Properties With Self in Swift
Why Can't I Pass a Protocol.Type to a Generic T.Type Parameter
Multiple Functions With the Same Name
Why My Return Is Nil But If I Press the Url in Chrome/Safari, I Can Get Data
Printing a Variable Memory Address in Swift
Checking If an Object Is a Given Type in Swift
How to Detect If an Skspritenode Has Been Touched
Protocol Doesn't Conform to Itself