我如何修改 class 的子 class 中的 属性?

how can I modify the property of a class in its subclasses?

class Animals{
    var name : String = "default"
    var age : Int = 0
    func Details()-> String{
        return "This animal is a \(name) and has \(age) years old."
    }
}


class Dogs : Animals{
    name = "dog"
}

class Cats : Animals{
    name = "cat"
}

var MyAnimal = Dogs()

我想看到这条消息:"This animal is a dog and has 0 years old." 但是每次我收到这个:"This animal is a default and has 0 years old."

var HisAnimal = Cats()

如果您想要存储属性而不是计算属性,您可以在 初始化程序 中设置 name,如下所示:

class Animal {
    let name: String
    var age: Int = 0
    /* designated initializer: fully initializes all instance properties */
    init(name: String) {
        self.name = name
    }
    func details() -> String {
        return "This animal is a \(name) and has \(age) years old."
    }
}


class Dog : Animal {
    /* designated initializer of subclass: must call a designated
       initializer from its immediate superclass                   */
    init() {
        super.init(name: "dog")
    }
}

class Cat : Animal {
    /* ... */
    init() {
        super.init(name: "cat")
    }
}

let myAnimal = Dog()

此机制确保 name 仅从一个地方设置,并显式传递给初始化程序。

一种解决方法是使用"template method pattern"

class Animals {
    lazy var name : String = self.defaultName
    var defaultName:String { return "default" }

    func Details()-> String{
        return "This animal is a \(name) and has \(age) years old."
    }
}

class Dogs : Animals {
    override var defaultName:String { return "dog" }
}

另一种方法是为每个子类创建 init 方法并覆盖默认值

这是我的做法,通过创建一个初始化程序来设置名称(使用默认值):

class Animal: CustomStringConvertible {
    let name: String
    let age: Int

    init(name: String = "default", age: Int = 0) { // FIXME: Does a default age of 0 make sense?
        self.name = name
        self.age = age
    }

    public var description: String {
        return "This animal is a \(name) and is \(age) years old."
    }
}


class Dog: Animal {
    init() {
        super.init(name: "Dog")
    }
}

class Cat: Animal {
    init() {
        super.init(name: "Cat")
    }
}

var myAnimal = Dog()
print (myAnimal)

或者在 classes

上使用协议(和泛型)

您很可能会创建 Cat:s 和 Dog:s 的实例,但您可能不想为抽象怪异的 Animal:s 这样做。 Animal 作为一个普通的 superclass 的另一种选择是让它成为一个协议。

protocol Animal {
    static var species: String { get }
    var name: String? { get }
    var age: Int { get set }
    var details: String { get }
}

extension Animal {
    static var species: String { return "\(self)".lowercased() }

    // since all the properties used in 'details' are blueprinted,
    // we might as well supply a default implementation of it.
    var details: String {
        return "This animal is a \(Self.species)\(name.map{ " named \([=10=])" } ?? ""), aged \(age)."
    }
}

struct Dog: Animal {
    let name: String?
    var age: Int

    init(age: Int, name: String? = nil) {
        self.name = name
        self.age = age
    }
}

var myDog = Dog(age: 3, name: "Fred")
print(myDog.details) // This animal is a dog named Fred, aged 3.
myDog.age = 4 // grats to Fred!
print(myDog.details) // This animal is a dog named Fred, aged 4.

let wildDog = Dog(age: 6) // never named ...
print(wildDog.details) // This animal is a dog, aged 3.

请注意,我选择使用class 属性 species来命名每个动物的物种,并保留实例属性 name 对于那些有名字的动物;比如说,你亲爱的狗 Fred(而不是你亲爱的狗 dog)。

使用协议也会自然而然地选择泛型而不是类型化抽象类型(后者在使用通用 superclass 时可能很诱人):

struct Cat: Animal {
    let name: String?
    var age: Int

    init(age: Int, name: String? = nil) {
        self.name = name
        self.age = age
    }
}

var wildCat = Cat(age: 2)

func yieldBirthday<T: Animal>(for animal: inout T) {
    print(animal.details)
    animal.age += 1
    print("This \(T.species) now had a birthday!")
    print(animal.details)
}

yieldBirthday(for: &myDog)
/* This animal is a dog named Fred, aged 4.
   This dog now had a birthday!
   This animal is a dog named Fred, aged 5. */

yieldBirthday(for: &wildCat)
/* This animal is a cat, aged 2.
   This cat now had a birthday!
   This animal is a cat, aged 3. */