Subclass inherit from class not able to access properties

I have defined the following class and subclass:


import Foundation
class Node {

    var key : String

    private weak var parent: Node?
    private var children: [Node] = []

    init(key : String) {
        self.key = key
    }

    func addChildNode(child: Node) {
        children.append(child)
        child.parent = self
    }

    func rootNode() -> Node {
        var node = self
    
        while node.parent != nil {
            node = node.parent!
        }
        return node
    }
}

class SubNode : Node  {
    var time : [Double] = []
}


When using the subclass and the method rootNode inherited, this returns the class Node and not the subclass SubNode. Therefore, I can not access the time property. One option is of course to cast explicitily the outcome of the rootNode as SubNode. But this is not very elegant. Any other idea?


var a = SubNode(key: "a")
a.time = [11,2,3]
var b = SubNode(key: "b")
var c = SubNode(key: "c")
a.addChildNode(b)
b.addChildNode(c)
c.rootNode() // c.rootNode can not access time unles casted as SubNode

Accepted Reply

Swift doesn't have "covariance" for return types (yet?), and that's what would let you override rootNode in a subclass to return a SubNode. That aside, the problem is, of course, that an instance of SubNode can't be sure that it's returning a SubNode root, because it doesn't control the type of the stored properties that have nodes for values.


You can work around the problem, clumsily, by giving SubNode a "subRootNode" property, if you don't want to force clients of the class to do a cast.


A better approach, in the Swift philosopy, is to not use class inheritance at all. Instead, you'd declare a NodeProtocol that defines the shared behavior of all nodes, and put the base class behavior in a protocol extension that implements default behavior. You can still have a Node base class if you want, but ideally you don't need it, and your (no-longer-sub-)classes would just implement their versions of the protocol behavior.


This is known as "protocol oriented programming", and you should find a lot of discussion of it if you search the web. (Also see the WWDC 2015 video on the subject.)


However, it's a bit of a paradigm shift for people who are used to subclassing in cases like this. It may take a bit of effort to get comfortable with the techniques.

Replies

As proposed in by others, I tried

    func rootNode() -> Self {
      
        var node = self
      
        while node.parent != nil {
            node = node.parent!
        }
      
        return node
    }


but I get a compiling error "Cannot assign value of type 'Node' to type 'Self' on line 06...

Swift doesn't have "covariance" for return types (yet?), and that's what would let you override rootNode in a subclass to return a SubNode. That aside, the problem is, of course, that an instance of SubNode can't be sure that it's returning a SubNode root, because it doesn't control the type of the stored properties that have nodes for values.


You can work around the problem, clumsily, by giving SubNode a "subRootNode" property, if you don't want to force clients of the class to do a cast.


A better approach, in the Swift philosopy, is to not use class inheritance at all. Instead, you'd declare a NodeProtocol that defines the shared behavior of all nodes, and put the base class behavior in a protocol extension that implements default behavior. You can still have a Node base class if you want, but ideally you don't need it, and your (no-longer-sub-)classes would just implement their versions of the protocol behavior.


This is known as "protocol oriented programming", and you should find a lot of discussion of it if you search the web. (Also see the WWDC 2015 video on the subject.)


However, it's a bit of a paradigm shift for people who are used to subclassing in cases like this. It may take a bit of effort to get comfortable with the techniques.

This is how I finally implemented it

import Foundation
protocol NodeProtocol : class
{
    var key : String {get set}
    var parent : Self? { get set }
    var children : [Self] { get set }
   
    init(key : String)
   
    func addChildNode(child: Self)
    func rootNode() -> Self
}
extension NodeProtocol {
   
    func addChildNode(child: Self) {
        let node = self
       
        node.children.append(child)
        child.parent = node
    }
   
    func rootNode()-> Self {
        var node = self
       
        while node.parent != nil {
            node = node.parent!
        }
       
        return node
    }
   
}
final class SubNode : NodeProtocol {
   
    var key : String
   
    var parent: SubNode?
    var children: [SubNode] = []
   
    init(key : String) {
        self.key = key
    }
   
    var time : [Double] = []
   
}
var a = SubNode(key: "a")
a.time = [11,2,3]
var b = SubNode(key: "b")
var c = SubNode(key: "c")
a.addChildNode(b)
b.addChildNode(c)
var rootNode = c.rootNode()
print(rootNode.time)