7

Swift has no abstract classes and methods. Instead, it offers protocols.

That's fine when your classes are either fully abstract or fully concrete.

But what is the best 'Swift' way to implement an abstract class that also has concrete methods?

Pseudo-code example:

class Animal {
  abstract makeSound()
  abstract eyeCount()
}

class Mammal : Animal {
  override eyeCount { return 2 } // Let's assume all mammals have hard-coded 2 eyes...

class Cat : Mammal {
  override makeSound { print "Meow!" }
}

class Dog : Mammal {
  override makeSound { print "Woof!" }
}

In Mammal, I do want to implement the concrete method eyeCount() because all mammals have 2 hard-coded eyes (supposedly) and I don't want to re-implement it in dog and cat. However, makeSound() should only be implemented for Dog and Cat as mammals have varying voices.

How would you implement this in Swift? Thanks!

Hatchmaster J
  • 543
  • 1
  • 6
  • 15

4 Answers4

6

You can use Protocol Extensions to get the exact same behavior as with Abstract Classes: Checking if abstract methods are implemented in subclasses at compile time.

protocol Entity {
  // MARK: - Abstract methods
  func filename() -> String

  // MARK: - Traits
  func saveData(data: NSArray)
}

extension Entity {
  func saveData(data: NSArray) {
      // Do something and call:
      let filename = filename()
  }
}

Now you can implement the Entity protocol on a Subclass and the compiler will force you to implement filename() while the saveData() method is already implemented.

pommes
  • 181
  • 1
  • 4
  • 1
    Nice, but in an abstract class I could define variables and use them in funcs. Here I would have to declare the variables in each class implementing the protocol and access them from the extension via accessor methods, or is there a better way? – AmigoNico Mar 04 '16 at 04:53
  • Extensions can't have stored properties, so this approach is rather limited. – Raphael Dec 29 '16 at 12:48
5

I would implement it like this:

class AbstractAnimal
{
    // Fully abstract method
    func methodThatReturnsSomething() -> String  {
        fatalError("methodThatReturnsSomething() is abstract and must be overriden!");
    }

    func eyeCount() -> Int {
        return 2;
    }
}

fatalError prevents Xcode from complaining that abstract method methodThatReturnsSomething() doesn't actually return anything.

BadmintonCat
  • 9,416
  • 14
  • 78
  • 129
  • I really have trouble with things that declare to go wrong only during runtime. It makes code unreliable. Check @pommes answer or this one, for an implementation, that checks during compile time http://stackoverflow.com/a/39038828/2435872 – jboi Aug 19 '16 at 12:25
0

A common method to do this is to have an abstract class that has concrete methods in it. All classes can subclass this abstract class to inherit the concrete methods as well as abstract methods.

UIGuestureRecognizer and UIGuestureRecognizerSubclass is a very good example of this. Because this is intended for subclassing, this abstract class contains many internal concrete methods as well as subclassable abstract methods for you to implement.

This is a common pattern for many Swift and Objective-C projects.

Schemetrical
  • 5,506
  • 2
  • 26
  • 43
  • I tried to do something similar in Swift. However, I got a compiler error on "My UIGestureRecgonizerSubclass" because it did not implement the (required) methods declared in "My UIGestureRecognizer" protocol. – Hatchmaster J Jun 01 '15 at 13:43
  • 1
    You must implement the required methods if you decide to follow a protocol unless those methods are `@optional`. – Schemetrical Jun 01 '15 at 13:45
  • You're correct. I do, however, wish to avoid concrete 'leaf' classes where the optional methods from the Protocol are **not** implemented. This would lead to 'respondToSelector' calls - a code horror in my humble opinion. – Hatchmaster J Jun 01 '15 at 13:55
0

One way you could do this, as suggested by @BobDickinson in Abstract functions in Swift Language, is the following:

protocol Animal {
    var eyeCount: Int { get }
    func makeSound()
}

// Note - `Mammal` doesn't conform to `Animal`.
class Mammal {
    let eyeCount = 2
}

// Only `Cat` and `Dog` conform to `Animal`, so only they need to implement `makeSound`.
class Dog: Mammal, Animal {
    func makeSound() {
        println("Woof")
    }
}

class Cat: Mammal, Animal {
    func makeSound() {
        println("Meow")
    }
}
Community
  • 1
  • 1
ABakerSmith
  • 22,759
  • 9
  • 68
  • 78
  • That's a nice solution. However, I would rather avoid inheriting Mammal and Animal separately. A Mammal is an Animal, and therefore every subclass of of Mammal should be implementing Animal as well. – Hatchmaster J Jun 01 '15 at 13:35
  • Also, for abstraction sake, let's assume **eyeCount** is a function rather than a property with a getter. It was a poor choice of an example. – Hatchmaster J Jun 01 '15 at 13:36