0

This works:

protocol Walkable {
    init()
}

class Animal {
    init(someProperty: Int) {}
}

class WalkableAnimal: Animal, Walkable {
    required init() {
        super.init(someProperty: 0)
    }
}

class Person {
    init() {
        InitializeAWalkableAnimal(walkableAnimal: WalkableAnimal.self)
    }

    func InitializeAWalkableAnimal<T: Animal>(walkableAnimal: T.Type) where T: Walkable {
        let animal = walkableAnimal.init()
    }
}

However, I want to avoid the WalkableAnimal subclass entirely. I just want to create a Cat class that inherts from Animal and conforms to Walkable protocol. Furthermore, I can not pass Cat directly as parameter because the types are dynamic. I would expect something like this should work:

protocol Walkable {
    init()
}

class Animal {
    init(someProperty: Int) {}
}

class Cat: Animal, Walkable {
    required init() {
        super.init(someProperty: 0)
    }
}

class Dog: Animal, Walkable {
    required init() {
        super.init(someProperty: 1)
    }
}

typealias AnyWalkableAnimal = (Animal & Walkable).Type

class Person {
    init(anyWalkableAnimal: AnyWalkableAnimal) {
        // ERROR
        InitializeAWalkableAnimal(walkableAnimal: anyWalkableAnimal.self)
    }

    func InitializeAWalkableAnimal<T: Animal>(walkableAnimal: T.Type) where T: Walkable {
        let animal = walkableAnimal.init()
    }
}

class PersonCaller {
    init() {
        Person(anyWalkableAnimal: Cat.self)
        Person(anyWalkableAnimal: Dog.self)
    }
}

The error is:

Instance method 'InitializeAWalkableAnimal(walkableAnimal:)' requires that 'Animal' conform to 'Walkable'

This is nonsense since the typealias doesn't allow types that aren't Walkables right? Why is the compiler not happy? Is there any way I can just pass around any type that conforms to both Animal and Walkable? Why is the typealias not working? The typealias explictily mentions that the type that it takes must be an Animal and Walkable. It works the other way around (as expected): when I try to pass in an Animal that does not conform to Walkable, the initializer gives a compile error.

I am developing on the Swift Development Toolchain 25 December 2018 if that makes any difference.

J. Doe
  • 12,159
  • 9
  • 60
  • 114
  • [Protocols don't always conform to themselves](https://stackoverflow.com/a/43408193/2976878) – in your case, `Walkable` cannot conform to itself because it has an `init()` requirement. Consider what should happen if you called `T.init()` within `InitializeAWalkableAnimal`, what type should be constructed? `T` would be of type `Animal & Walkable`, which doesn't provide a static type that can be initialised conforming to `Walkable`. – Hamish Feb 06 '19 at 19:31

1 Answers1

2

I don't see why InitializeAWalkableAnimal needs to be generic at all.

protocol Walkable {
    init()
}

class Animal {
    init(someProperty: Int) { }
}

class Cat: Animal, Walkable {
    required init() { super.init(someProperty: 0) }
}

class Dog: Animal, Walkable {
    required init() { super.init(someProperty: 1) }
}

typealias Pet = Animal & Walkable
typealias PetType = Pet.Type

class Person {
    init(petType: PetType) {
        initPet(petType: petType)
    }

    func initPet(petType: PetType) {
        let pet = petType.init()
        print("I got a pet: \(pet)")
    }
}

class PersonCaller {
    init() {
        Person(petType: Cat.self)
        Person(petType: Dog.self)
    }
}

_ = PersonCaller()

Output:

I got a pet: __lldb_expr_8.Cat
I got a pet: __lldb_expr_8.Dog
rob mayoff
  • 375,296
  • 67
  • 796
  • 848