2

There is no abstract class in Swift.

People have alternatives ways to have similar mechanism. But they don't answer my question. How to build a family of classes with some identical functions and some different functions?

Abstract classes in Swift Language suggests that we use protocol instead of abstract base class. But protocol isn't a place we could write function body, so for the same function in every subclass, we would have to write the same body as many time as the subclass number.

What I want is something like this:

protocol Animal : class {
    func run()
    var legs : [Leg] { get }

    // this method is invalid in swift protocol
    // as protocol methods must have no bodies.
    // Instead, the same method will be repeated
    // in every subclasses

    func legCount() -> Int {
       return self.legs.count
    }
}

class Dog : Animal {
    func run() {}
    var legs : [Leg]
}

class Bird : Animal {
    func run() {}
    var legs : [Leg]
}
Community
  • 1
  • 1
thats2ez
  • 21
  • 2
  • 2

1 Answers1

2

Here's one way I might do it:

  1. Declare a protocol AnimalType. In it, define what an animal is and what it can do, but not how it does anything. This naming convention is used all over the Swift standard library: CollectionType, SequenceType, IntegerType, BooleanType, etc.
  2. Declare a class Animal that defines how all animals do the things that they have in common; for everything else, just make placeholder functions or properties (this is your "abstract" class). If you have a function that doesn't make sense to call on your abstract class, call fatalError() in it's body.
  3. Create your specific class Dog, class Bird, etc. and override functions and/or add new ones as needed.

Something like this:

struct Leg { } // Just so it'll run in a Playground

protocol AnimalType: class {
    func run()
    var legs : [Leg] { get }
    func legCount() -> Int
}

class Animal: AnimalType {
    func run() {
        fatalError("run() can not be called on the Animal class")
    }

    var _legs: [Leg]! = nil
    var legs: [Leg] { get { return _legs } }

    func legCount() -> Int {
        return legs.count
    }
}

class Dog: Animal {
    override func run() {
        println("Running Dog!")
    }

    override init() {
        super.init()
        _legs = [Leg](count: 4, repeatedValue: Leg())
    }
}

class Bird : Animal {
    override func run() {
        println("Running Bird!")
    }

    override init() {
        super.init()
        _legs = [Leg](count: 2, repeatedValue: Leg())
    }
}

Then, if you need say an Array of animals, declare the array using the protocol, not the Animal class:

let animals: Array<AnimalType>

Trying some stuff out:

let dog = Dog()
println("Dogs have \(dog.legCount()) legs.")
dog.run()

let bird = Bird()
println("Birds have \(bird.legCount()) legs.")
bird.run()

Which will output:

Dogs have 4 legs. 
Running Dog! 
Birds have 2 legs. 
Running Bird!

The Array also works:

var animals: Array<AnimalType> = [dog, bird]
var legs = animals.map { $0.legCount() }
println(legs)

[4, 2]

And, while Animal can still be instantiated:

let a = Animal()

Calling run() on it will be a fatal error:

a.run()

fatal error: run() can not be called on the Animal class: file <EXPR>, line 18

Mike S
  • 41,895
  • 11
  • 89
  • 84
  • What's the benefit of having an `AnimalType` protocol? – Aaron Brager Oct 29 '14 at 04:27
  • IE, you could just use `let animals : [Animal] = …` and I don't think you lose anything from that. – Aaron Brager Oct 29 '14 at 04:38
  • 2
    @AaronBrager true, `[Animal]` would work just fine. In general I'd say that it allows for different base classes that implement the same protocol. In this case specifically though, there probably isn't much point in having it. – Mike S Oct 29 '14 at 04:58
  • 2
    `class GreatCrystallineEntity: AnimalType { func legCount() -> Int { fatalError("Crystalline Entities don't have legs") } }` – Aaron Brager Oct 29 '14 at 05:01
  • @AaronBrager nice :) – Mike S Oct 29 '14 at 05:22
  • @MikeS The fatalError solution will postpone the crash until user run it. This crashing behavior is not desired, especially for a compiled language like Swift, where type checking is commonly done in pre-compile phase. Is there any graceful way that we could prevent the base class being instantiated and run method when compiling? – thats2ez Oct 29 '14 at 06:10
  • @thats2ez that's true about the `fatalError`; it would certainly be nicer if there was something we could catch at compile time. As for preventing the base class from being instantiated, you could declare `Animal`'s `init` function as `private`, but that only works if all of its subclasses are defined in the same file because otherwise they wouldn't be able to call `super.init()`. You could also relax the restriction and declare `init()` as `internal`, but then all the subclasses have to be in the same module instead of the same file. It's all about trade-offs really. – Mike S Oct 29 '14 at 16:04