Unable to call initializer for subclass of generic type
With a lot of help from the answer originally posted by @rintaro, I was able to solve this problem, although there is still an oddity that I will post under a separate question.
As it turns out, @rintaro was absolutely correct in the need to initialize the instance of the generic type using the following syntax:
let result = (T.self as T.Type)(myRecord)
This works as long as the base class MyBaseClass
declares this initializer with a required
tag:
public class MyBaseClass {
public required init(_ record:MyRecordType) {
...
}
}
and the subclass MySubClass
implements the matching initializer:
public class MySubClass : MyBaseClass {
public required init (_ record:MyRecordType) {
...
super.init(record)
}
}
Where things fail, however is when I actually have a 3-level class hierarchy and the initializer hierarchy throughs an override
into the mix. To envision this, consider a set of class that represents nodes in a tree structure:
public class Node {
public init(_ record:MyRecordType) {
...
}
}
public class RootNode : Node {
override public init(_ record:MyRecordType) {
...
super.init(record)
}
public class func <T:RootNode>retrieveAll(success:(T) -> ()) {
// Retrieve all instances of root node subclass T, and invoke success callback with new T instance
}
}
public class ChildNode : Node {
public init(_ record:MyRecordType, parentNode:Node) {
...
super.init(record)
}
public class func <T:ChildNode>retrieveChildren(parent:Node, success:(T) -> ()) {
// Retrieve all child T instances of parent node, and invoke success callback with new T instance
{
}
The problem occurs in the implementation of the RootNode
class's retrieveAll
method. In order for it to work as described by @rintaro, I need the init
in RootNode
to be marked with the required
keyword. But because it also overrides the initializer from Node
, it also needs to have the override
keyword. So I try to use both keywords in the declaration:
override required public init(_ record:MyRecordType) {
...
}
The Swift compiler accepts this, but when I use it to initialize an instance from within the retrieveAll
method, it crashes with a BAD_ACCESS.
I was able to work around this problem by changing the method signature of the NodeClass
just slightly so that its RootNode
subclass doesn't need to override:
public class Node {
public init(record:MyRecordType) {
...
}
}
public class RootNode {
public required init(_ record:MyRecordType) {
...
super.init(record:record)
}
}
By doing this workaround, my subclasses of RootNode
can properly implement the required initializer, and the retrieveAll
method in RootNode
can properly instantiate instances of those subclasses.
Inheriting initializer from generic class
This now works in Swift 3. My original example now compiles. There is no mention of this in the Swift 3 Language changes, so I can only assume that this was a bug.
override initializer from generic class
I am not sure what exactly goes wrong for you. When I run your code everything seems fine. Are you sure you're instantiating your instances correctly?
let checkItem = CheckItem("Check item data") { (string: String) -> String in
return string + "checkItem"
}
let labelItem = LabelItem<String, String, Int>("Label item") { (string: String) -> String in
return string + "labelItem"
}
let dataTableItem = DataTableItem<String, String, Int>("Data table item") { (string: String) -> String in
return string + "dataTableItem"
}
In LabelItem
and DataTableItem
you have a generic V
which is not used anywhere and is not a parameter, so you need to be explicit with your types upon instantiation since you're not passing the V
type in the init and the compiler can't infer the type. Thus <String, String, Int>
or any other types that meet the constraints.
EDIT:
After looking into your project code (the project didn't run on my Xcode, I only copied relevant code into my project) I still see no problem - both initializers of CheckItem
compile:
open class TableItem: NSObject {
public var title: String?
}
open class DataTableItem<T, U: Equatable & CustomStringConvertible, V: CustomStringConvertible>: TableItem{
let data: T
let getter: (T) -> U
public weak var host: TableItemHost?
public init(_ data: T, getter: @escaping (T) -> U) {
self.data = data
self.getter = getter
}
}
public protocol TableItemHost: class {}
open class CheckItem<T, U: Equatable & CustomStringConvertible>: DataTableItem<T,U,Bool> {
public init(_ data: T, host: TableItemHost, getter: @escaping (T) -> U) {
super.init(data, getter: getter)
self.host = host
}
public override init(_ data: T, getter: @escaping (T) -> U) {
super.init(data, getter: getter)
}
}
Creating instances:
let checkItem1 = CheckItem("Check item 1 ") { (string: String) -> String in
return string
}
class Host: TableItemHost {}
let host = Host()
let checkItem2 = CheckItem("Check item 2 ", host: host) { (string: String) -> String in
return string
}
print(checkItem1.data)
print(checkItem2.data)
Copy paste my code into a playground and see for yourself. Perhaps there is something other than the initializer causing the error.
For a test you can also try commenting out both initializers of CheckItem
and instantiate it with the inherited initializer. That should work, because CheckItem
will not have its own designated initializer anymore (https://stackoverflow.com/a/31459131/1433612)
Calling constructor of a generic type
There is no way to use the Java type system to enforce that a class hierarchy has a uniform signature for the constructors of its subclasses.
Consider:
public class ColorPencil extends Pencil
{
private Color color;
public ColorPencil(Color color)
{
super();
this.color=color;
}
}
This makes ColorPencil a valid T (it extends Item). However, there is no no-arg constructor for this type. Hence, T() is nonsensical.
To do what you want, you need to use reflection. You can't benefit from compile-time error checking.
How a generic class to conform a protocol (that have init in it) with constraint?
I don't know of a way to achieve this without making Graph final
. If you do make it final
, however, it will work fine as long as you remove required
(required
doesn't mean anything if there can't be subclasses):
extension Graph: ExpressibleByArrayLiteral where D == Int {
convenience init(arrayLiteral elements: Node...) {
self.init(Array(zip(0..., elements)))
}
}
Python-3.5 typing.Generic subclass never calls `__init__`
Just run it with Python 3.5.2 64bit on Windows, and it just works fine!
If it was a bug, it's fixed now.
Output:
- main.Foo<~T> foo
- main.Foo<~T> bar
Related Topics
Swift: Uicollectionview Selecting Cell Indexpath Issues
Nspredicate with Swift and Core Data
Using Foreach with a an Array of Bindings (Swiftui)
Swift Draw Shadow to a Uibezier Path
Can't Create an Array of Types Conforming to a Protocol in Swift
Uitableviewcell Height Auto Layout Not Working on iOS 10
Proper Model for Multiple Alamofire Requests for Multiple Websites
How to Remove Quotes from String on Swift
Swift Uifont Ibinspectable - Is It Possible
How to Get the Yaw, Pitch, Roll of an Aranchor in Absolute Terms
How to Recognize Continuous Touch in Swift
"Missing Required Entitlement" for Nfctagreadersession
How to Highlight a Uitextview's Text Line by Line in Swift
Swift - Take Nil as Argument in Generic Function with Optional Argument
Swiftui: Dismiss View Within MACos Navigationview