为什么在循环中检查类型时需要类型转换?

Why is typecasting necessary when checking types in loops?

我试图更深入地理解类型转换,但不明白为什么在这个例子中有必要:

class Animal { }
class Fish: Animal { }

class Dog: Animal {
    func makeNoise() {
        print("Woof!")
    }
}

let pets = [Fish(), Dog(), Fish(), Dog()]

我正在阅读以检查上述数组中的类型,我需要 运行 以下代码:

for pet in pets {
    if let dog = pet as? Dog {
        dog.makeNoise()
    }
}

并且以下代码会崩溃:

for pet in pets {
    if pet == Dog() {
        pet.makeNoise()
    }
}

它returns出现以下错误:

error: Learn Swift.playground:24:13: error: value of type 'Animal' has no member 'makeNoise'
        pet.makeNoise()
        ~~~ ^~~~~~~~~

这是为什么?最后一段代码不会只是检查数组中的项目是否是 Dog() 吗?

目前,我的猜测是我没有完全理解for循环中"pet"的隐式类型。我假设它只是根据数组中项目的类型猜测类型,在我们的例子中是一个对象。

检查数组的类型pets:应该是[Animal]/。动物不能发出声音,因此您需要明确检查它是否是狗。本质上,通过将 Dog 与其他 Animals 装箱在异构数组中,您丢失了它的类型。在 Animal 上调用 makeNoise() 没有意义,因此编译器会强制您在调用 makeNoise().[=17 之前检查数组的元素实际上是 Dog =]

当你运行这一行

print(type(of: pets))

显示Array<Animal>。基数 class 是数组中各项的公分母。

在你的第二个例子中你应该得到另一个编译器错误

Binary operator '==' cannot be applied to operands of type 'Animal' and 'Dog'

然而没有类型转换 pet 仍然是 Animal'Animal' 没有成员 'makeNoise'

首先,检查实例类型时使用is键盘。例如if pet is Dog { ... },您目前只是在冗余地初始化它的一个新对象。

那么,当您询问 if pet is Dog { ... } 时,您只是根据该语句检索布尔值,但它不会使编译器将 pet 引用为 Dog.
相反,当您说 if let dog = pet as? Dog { ... } 时,您正在创建类型 Dog? 的新值,这使得编译器将其称为 Dog.