I've been playing around with Swift protocols and I'm trying to figure out why this code isn't working...
protocol Animal {
var name: String {get}
var breed: String {get}
}
struct Bird: Animal {
var name: String
var breed: String
var wingspan: Double
}
protocol AnimalHouse {
var myAnimal: Animal! {get set}
}
class Birdhouse: AnimalHouse {
var myAnimal: Bird!
func isOpeningBigEnough() -> Bool {
return myAnimal.wingspan <= 5.0
}
}
The problem the compiler keeps giving me is that that BirdHouse
doesn't conform to protocol AnimalHouse
. If you follow up, it'll tell you that myAnimal
requires type Animal
, and I'm supplying type Bird
. Obviously, Bird
does conform to the Animal
protocol, but that's not enough to make the compiler happy.
I'm assuming this is one of those one-line fixes where the trick is knowing where the one line is. Anybody have any suggestions?
(And, yes, I could make myAnimal
an Animal
and then cast it as a Bird
later in the function, but that seems unnecessarily messy.)
The compiler is right.
When you write
protocol AnimalHouse {
var myAnimal: Animal! {get set}
}
you are making (among the others) the following statement:
If a type does conform to
AnimalHouse
, then it is possible to put anAnimal!
inside themyAnimal
property.
Now let's look at how Birdhouse
is defined
class Birdhouse: AnimalHouse {
var myAnimal: Bird!
...
}
The type on myAnimal
is Bird!
. And you cannot put an Animal!
inside a property of type Bird!
.
So Birdhouse
does not respect what promised in the AnimalHouse
protocol.
As you said yourself in the question, you can't just downcast to Bird
from Animal
. I propose changing the var to be optional, as an AnimalHouse
is likely to be without inhabitant some of the time.
In my implementation below non Bird
animals can't enter the birdhouse.
protocol AnimalHouse {
var myAnimal: Animal? {get set}
}
class Birdhouse: AnimalHouse {
var myAnimal: Animal? {
get{
return myBird
}
set(newanimal){
if let bird = newanimal as? Bird {
myBird = bird
}
}
}
private var myBird: Bird?
func isOpeningBigEnough() -> Bool {
return myBird?.wingspan <= 5.0
}
}
A further development of the AnimalHouse
protocol might be to add throws
to the setter ( not possible as of Swift 2.0 ) or that an AnimalHouse
returns the type of animal it can house.
protocol AnimalHouse {
var myAnimal: Animal? {get set}
func houses() -> Any
}
class Birdhouse: AnimalHouse {
func houses() -> Any {
return Bird.self
}
}
Maybe you will be satisfied with such an approach:
protocol Animal {
var name: String {get}
var breed: String {get}
}
struct Bird: Animal {
var name: String
var breed: String
var wingspan: Double
}
// Read from here
protocol House {
typealias Inhabitant
var inhabitant: Inhabitant! {get set}
}
class Birdhouse: House {
typealias Inhabitant = Bird
var inhabitant: Inhabitant!
func isOpeningBigEnough() -> Bool {
return inhabitant.wingspan <= 5.0
}
}
But then the 'House' protocol can only be used as a generic constraint, ie the following is impossible:
let house: House = Birdhouse() // Compile-time error
But you can do the following:
func printHouseInhabitant<T: House>(house: T) {
print(house.inhabitant)
}
let house = Birdhouse()
house.inhabitant = Bird(name: "Unnamed", breed: "general bird", wingspan: 4.5)
printHouseInhabitant(house) // "Bird(name: "1", breed: "2", wingspan: 3.0)\n"
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.