I have a class Node which has a bunch of child classes, BinaryNode is one of them. In a function I pass a node object as an argument, with intent of checking which child of the node class it is and performing operations based on which one it is.
It is throwing errors that Value of type 'Node' has no member '_____' despite those values being a part of the subclass already selected by an if statement. Its best to just see the code directly.
I'm guessing the error has something to do with the order of passing a parent class object in and expecting swift to understand that it might be a subclass too, any feedback is appreciated!
Class Declaration:
public class Node {
var spec: String
var val: String
init(spec: String, val: String) {
self.spec = spec
self.val = val
}
}
// required to differentiate between Nodes
extension Node: Equatable {
public static func ==(lhs: Node, rhs: Node) -> Bool {
return lhs.spec == rhs.spec && lhs.val == rhs.val
}
}
class UnaryNode: Node {
var child: Node
init(spec: String, val: String, child: Node) {
self.child = child
super.init(spec: spec, val: val)
}
}
class BinaryNode: Node {
var leftChild: Node
var rightChild: Node
init(spec: String, val: String, lChild: Node, rChild: Node) {
self.leftChild = lChild
self.rightChild = rChild
super.init(spec: spec, val: val)
}
}
And the part with the error:
public func printFunction(root: Node?) {
if let node = root {
if node is BinaryNode {
let left = node.leftChild
//Value of type 'Node' has no member 'leftChild'
let right = node.rightChild
//Value of type 'Node' has no member 'rightChild'
//...
} else if node is UnaryNode {
let child = node.child
//Value of type 'Node' has no member 'child'
//...
}
}
You might need public
on those declarations, but that's not the cause of this particular error. You have code like this:
if node is BinaryNode {
let left = node.leftChild
The problem here is that although you've checked that the object referred to by node
is a BinaryNode
, the node
variable itself is still of type Node
, which doesn't have a leftChild
. Instead, try writing your code like this:
if let binaryNode = node as? BinaryNode {
let left = binaryNode.leftChild
That creates a new variable of type BinaryNode
which refers to the same object as node
. However, it now has the correct type, so you can retrieve the leftChild
.
If you prefer, you don't have to use a different name for the new variable. This is called "shadowing" a variable, and looks like this:
if let node = node as? BinaryNode {
let left = node.leftChild
There are actually two node
s here: the "outer" one of type Node
, and the "inner" one of type BinaryNode
. This is perfectly acceptable, idiomatic Swift. Some programmers find it confusing to have two variables with the same name. Other programmers find it clearer to use the same name for two variables referring to the same object. Chose whichever approach you prefer for your code.
Incidentally, the behavior you had in mind in your original question, where node
automatically had type BinaryNode
after the if
test, is called "type narrowing". It's a feature that the Swift language currently does not have, although other languages do.