Swift: Overriding vs Overloading (Xcode 6 GM)


Overriding and overloading

There are two terms to become familiar with here: overriding and overloading. Overriding is when you create a subclass with a function identical (or near identical) to one in its superclass. Whereas overloading is when a function of the same name takes a different type of argument. This is illustrated here:
class A {
    func aB(a:String){
    }
}
class B: A {
    override func aB(a:String){
        // overriding
    }
    func aB(a:Int) {
        // overloading
    }
}

var a = B()
a.aB(10)
a.aB("hello world")
Note: often you'll hear the term operator overloading being used and this follows the same principle  but as the name suggests applies directly to operators.

Overloading a method with the same argument type

When you call a class method in Swift, you don't use the parameter name of the first argument (unless you've applied the # modifier). Whereas with an init method you always use the first parameter names. This leads to some difference in behaviour between overriding and overloading in methods with a single argument, as we'll see in the discussion of init methods and 'multiple arguments', but for now let's consider a superclass containing the following method:
 func aB(a:String){
    }
A subclass treats any method of the same name that takes the same argument as an override not an overload and use of the override keyword is forced (no matter what we label the parameter).
 override func aB(c:String){
    }
And while the compiler allows us to circumvent warnings by adding a hash sign to the parameter name
 func aB(#c:String){
    }
it won't allow us to call the seemingly "overloaded" method and forces us to use the method from the superclass. This in effect means that this is something you shouldn't do and instead you should accept the need to override when you have a method that accepts one argument of a single type (or create a separate method).

Similar but different

A similar thing happens if we change the parameter name but not the type of the argument in an init method.
class A {
    func aB(a:String){
    }
    init (a:String) {
        
    }
    
}
class B: A {
    override func aB(c:String){
        // uses a different parameter name but is still overriding
    }
    init (b:String) {
        super.init(a: b)
        // not an override but blocks access to the superclass init method
    }
}

var a = B(a:"Hello") // error
var a = B(b:"Hello") // OK
An override situation occurs with the init method where it blocks the superclass alternative, rather than using it (as a regular method does), and as a programmer you need to be aware that in most (if not all) cases you would want to include an override modified version of the original function as well as the ambiguous pseudo-override version.
class A {
    func aB(a:String){
    }
    init (a:String) {
        
    }
    
}
class B: A {
    override func aB(c:String){
        // uses a different parameter name but is still overriding
    }
    init (b:String) {
        super.init(a: b)
        // not an override but an alternative init method
    }
    override init (a:String) {
        super.init(a: a)
        // an override of the superclass init method
    }
}

var a = B(a:"Hello") // OK
var b = B(b:"Hello") // OK
Once the first override has been performed it is then possible to create as many different init methods that take the same type as you like.

Multiple arguments

In contrast to single-argument methods, methods with multiple arguments of the same type can be overloaded using different parameter names, as you'll see here:
class A {
    var a:String
    func aB(a:String){
    }
    func aC(a:String,b:String){
    }

    init (a:String) {
    self.a = a
    }
    
}
class B: A {
    
    func aC(a:String,c:String){
    }

    init (b:String) {
        super.init(a: b)
        // not an override but alternative init method
    }
    override init (a:String) {
        super.init(a: a)
        // an override of the superclass init method
    }

}

var a = B(a:"Hello") // OK
var b = A(a:"Hello") // OK

a.aC("Hello", b: "Swift") // overloading a method using different parameter names // OK
a.aC("Hello", c: "Swift") // OK
There is no blocking or interference in the inheritance as there is with single arguments.

Beyond methods

Further, subscripts behave almost as you'd expect when being overridden and overloaded
class A {
    var a:String
    func aB(a:String){
    }
    init (a:String) {
    self.a = a
    }
    subscript(index: Int) -> Int {
        return distance(advance(self.a.startIndex, index),self.a.endIndex)
            // calculate distance from subscript index to end of string a
    }
    
}
class B: A {
    override func aB(c:String){
        // uses a different parameter name but is still overriding
    }
    init (b:String) {
        super.init(a: b)
        // not an override but blocks access to the superclass init method
    }
    override init (a:String) {
        super.init(a: a)
        // not an override but blocks access to the superclass init method
    }
    override subscript(index: Int) -> Int {
        return distance(self.a.startIndex,advance(self.a.startIndex, index))
            // calculate distance from start of string a to subscript index
    }
    
    subscript(index:Character)->String.Index? {
    return find(self.a, index)?
        // overloading
    }
}

var a = A(b:"Hello") // OK
var b = B(a:"Hello") // OK

var c = a[1]
c // returns 4

var d = b[1]
d // returns 1
var e = b["e"]! // returns a String.Index of 1

but while the override behaviour of subscripts is the same as regular methods when working with a single argument, for multiple arguments of the same type (as a subscript in the superclass) you must always use the override keyword (whatever the parameter names) there is no overloading of the subscripts using the same types as there is with multiple-argument methods.

Computed Properties

Computed properties and properties that hold closures can be overridden as well. For example a superclass with the following property
var b:String {
        return a
    }
might have a subclass with something like this:
override var b:String {
    get {
        return a
    }
    set {
        a = newValue
    }
}
Note: This applies whether the computed properties are of an instance or the class itself.

Stored Properties

It isn't possible to override or overload a stored property in the same sense that one can override a method. However it is possible to add property observers to a stored property through a subclass override.
override var a:String {
    willSet(nv) {
        println("new value: \(nv)")
    }
    didSet(ov) {
        println("old value: \(ov)")
    }
        
}

Conclusion

I set out to write this blogpost with it in my mind that I already knew how overrides and overloads behaved, but as the writing progressed it became clear that there were nuances and things did not all work as I had at first expected. I ended up tinkering with the edges of overloading and overriding and finding things that are currently true (but might in the future change). Things that the compiler didn't query but that it also didn't know how to execute properly. Things like overloading methods with a single parameter of the same type.

The post itself has ended up a bit jagged and I hope in some future form to rewrite with greater clarity, but for now I'm releasing it as a record of my findings rather than as a complete instruction guide to overloading and overriding.

Comments

  1. So basically overiding is where things are added to a function but the original function still partly stays the same?
    And Then overloading is changing the function completely and just keeping the same function name, right?

    ReplyDelete
    Replies
    1. Overloading is when a method of the same name takes different types, whereas overriding is when a method in a subclass overrides (i.e. replaces) the one in the superclass.

      Delete
  2. This comment has been removed by a blog administrator.

    ReplyDelete

Post a Comment