Swift协议:为什么编译器会抱怨我的类不符合协议?

时间:2015-11-19 05:13:08

标签: swift protocols

我一直在使用Swift协议,我正在试图弄清楚为什么这段代码不起作用......

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
  }
}

编译器给我的问题是BirdHouse不符合协议AnimalHouse。如果您进行了跟进,它会告诉您myAnimal需要输入Animal类型,而我正在提供类型Bird。显然,Bird符合Animal协议,但这还不足以使编译器满意。

我假设这是一个单行修复中的一个,其中诀窍是知道一行的位置。有人有什么建议吗?

(而且,是的,我可以将myAnimal设为Animal,然后在函数中将其转换为Bird,但这似乎不必要地混乱。)

3 个答案:

答案 0 :(得分:3)

编译器是对的。

写作时

protocol AnimalHouse {
    var myAnimal: Animal! {get set}
}

您正在(以及其他人中)发表以下声明:

  

如果某个类型符合AnimalHouse,则可以放置一个   Animal!属性中的myAnimal

现在让我们看看如何定义Birdhouse

class Birdhouse: AnimalHouse {
    var myAnimal: Bird!

    ...
}

myAnimal上的类型为Bird!。 并且您不能将Animal!放在Bird!类型的属性中。

因此Birdhouse 尊重AnimalHouse协议中承诺的内容。

答案 1 :(得分:2)

正如你在问题中所说的那样,你不能只从Bird转发到Animal。我建议将var更改为可选,因为AnimalHouse可能在某些时候没有居民。

在我的实施中,非Bird动物无法进入禽舍。

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
    }
}

AnimalHouse协议的进一步发展可能是将throws添加到setter(not possible as of Swift 2.0),或者AnimalHouse返回它可以容纳的动物类型。

protocol AnimalHouse {
    var myAnimal: Animal? {get set}
    func houses() -> Any
}

class Birdhouse: AnimalHouse {
    func houses() -> Any {
        return Bird.self
    }
}

答案 2 :(得分:1)

也许你会对这种方法感到满意:

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
    }
}

然后是&#39; House&#39;协议只能用作通用约束,即以下是不可能的:

let house: House = Birdhouse() // Compile-time error

但您可以执行以下操作:

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"