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

Swift protocols: Why does the compiler complain that my class doesn't conform to a protocol?

我一直在研究 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,但这似乎不必要地混乱。)

正如您在问题中所说,您不能只是从 Animal 向下转换为 Bird。我建议将 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 () 或者 AnimalHouse returns 它可以容纳的动物类型。

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

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

也许你会对这样的做法感到满意:

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

但是 'House' 协议只能用作通用约束,即不可能:

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"

编译器是对的。

写的时候

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

您正在做出(除其他外)以下声明:

If a type does conform to AnimalHouse, then it is possible to put an Animal! inside the myAnimal property.

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

class Birdhouse: AnimalHouse {
    var myAnimal: Bird!

    ...
}

myAnimal 上的类型是 Bird!。 而且您不能将 Animal! 放入 Bird!.

类型的 属性 中

所以 Birdhouse 尊重 AnimalHouse 协议中的承诺。