Переопределение сохраненного свойства в Swift


126

Я заметил, что компилятор не позволяет мне переопределить сохраненное свойство другим сохраненным значением (что кажется странным):

class Jedi {
    var lightSaberColor = "Blue"
}


class Sith: Jedi {
    override var lightSaberColor = "Red" // Cannot override with a stored property lightSaberColor
}

Однако мне разрешено делать это с помощью вычисляемого свойства:

class Jedi {
    let lightSaberColor = "Blue"
}


class Sith: Jedi {
    override var lightSaberColor : String{return "Red"}

}

Почему мне не разрешено присвоить ему другое значение?

Почему переопределение сохраненного свойства является мерзостью, а выполнение этого - кошерным? Что где они думают?


Ответы:


82

Почему мне не разрешено просто придать ему другое значение?

Вы определенно можете присвоить унаследованному свойству другое значение. Вы можете сделать это, если инициализируете свойство в конструкторе, который принимает это начальное значение, и передаете другое значение из производного класса:

class Jedi {
    // I made lightSaberColor read-only; you can make it writable if you prefer.
    let lightSaberColor : String
    init(_ lsc : String = "Blue") {
        lightSaberColor = lsc;
    }
}

class Sith : Jedi {
    init() {
        super.init("Red")
    }
}

let j1 = Jedi()
let j2 = Sith()

println(j1.lightSaberColor)
println(j2.lightSaberColor)

Переопределение свойства - это не то же самое, что присвоение ему нового значения - это больше похоже на присвоение классу другого свойства. Фактически, именно это и происходит, когда вы переопределяете вычисляемое свойство: код, вычисляющий свойство в базовом классе, заменяется кодом, который вычисляет переопределение для этого свойства в производном классе.

[Возможно ли] переопределить фактическое сохраненное свойство, т. lightSaberColorЕ. Имеющее другое поведение?

За исключением наблюдателей, сохраненные свойства не имеют поведения, поэтому там действительно нечего переопределять. Присвоение собственности другого значения возможно с помощью описанного выше механизма. Это именно то, что пытается достичь пример в вопросе, с другим синтаксисом.


2
@MaxMacLeod За исключением наблюдателей, сохраненные свойства не имеют поведения, поэтому там действительно нечего переопределять. Он хотел присвоить хранимому свойству другое значение в подклассе, но не был уверен в механизме его достижения. Ответ объясняет, как это можно сделать в Swift. Прошу прощения за поздний ответ, похоже, что ваш комментарий вызывает достаточно путаницы, чтобы привлечь отрицательные голоса, поэтому я решил объяснить, что происходит.
dasblinkenlight 09

55

Для меня ваш пример не работает в Swift 3.0.1.

Я ввел на детской площадке этот код:

class Jedi {
    let lightsaberColor = "Blue"
}

class Sith: Jedi {
    override var lightsaberColor : String {
        return "Red"
    }
}

Выдает ошибку во время компиляции в Xcode:

не может переопределить неизменное свойство let 'lightsaberColor' с получателем 'var'

Нет, вы не можете изменить тип сохраняемой собственности. Принцип замены Лискова вынуждает вас разрешить использование подкласса там, где требуется суперкласс.

Но если вы измените его на varи, следовательно, добавите setв вычисляемое свойство, вы можете переопределить сохраненное свойство вычисляемым свойством того же типа.

class Jedi {
    var lightsaberColor = "Blue"
}


class Sith: Jedi {
    override var lightsaberColor : String {
        get {
            return "Red"
        }
        set {
            // nothing, because only red is allowed
        }
    }
}

Это возможно, потому что имеет смысл переключиться с сохраненного свойства на вычисляемое.

Но переопределение сохраненного varсвойства на сохраненное varсвойство не имеет смысла, потому что вы можете изменить только значение свойства.

Однако вы можете вообще не заменять сохраненное свойство сохраненным свойством.


Я бы не сказал, что ситхи - джедаи :-P. Поэтому ясно, что это не может работать.


18
class SomeClass {
    var hello = "hello"
}
class ChildClass: SomeClass {
    override var hello: String {
        set {
            super.hello = newValue
        }
        get {
            return super.hello
        }    
    }
}

13
Хотя этот фрагмент кода может решить вопрос, включение объяснения действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причины вашего предложения кода.
DimaSan

15

Вероятно, вы захотите присвоить этому свойству другое значение:

class Jedi {
    var lightSaberColor = "Blue"
}


class Sith: Jedi {
    override init() {
        super.init()
        self.lightSaberColor = "Red"
    }
}

9
То же самое относится к комментарию выше
Макс МакЛауд

10

Для Swift 4 из документации Apple :

Вы можете переопределить унаследованное свойство экземпляра или типа, чтобы предоставить собственный пользовательский метод получения и установки для этого свойства, или добавить наблюдателей свойств, чтобы разрешить переопределяющему свойству наблюдать за изменением значения базового свойства.


7

В Swift это, к сожалению, невозможно. Лучшая альтернатива следующая:

class Jedi {
    private(set) var lightsaberColor = "Blue"
}


class Sith: Jedi {
    override var lightsaberColor : String {
        get {
            return "Red"
        }
    }
}

3

У меня была такая же проблема, чтобы установить константу для контроллера представления.

Поскольку я использую построитель интерфейса для управления представлением, я не могу его использовать init(), поэтому мой обходной путь был аналогичен другим ответам, за исключением того, что я использовал вычисляемую переменную только для чтения как в базовом, так и в унаследованном классах.

class Jedi {
    var type: String {
        get { return "Blue" }
    }
}

class Sith: Jedi {
    override var type: String {
        get { return "Red" }
    }
}

3

Если вы попытаетесь сделать это в Swift 5, вас встретит

Невозможно переопределить неизменяемое свойство let 'lightSaberColor' с помощью получателя 'var'

Лучше всего объявить его как вычисляемое свойство.

Это работает, поскольку мы просто переопределяем get {}функцию

class Base {
   var lightSaberColor: String { "base" }
}

class Red: Base {
   override var lightSaberColor: String { "red" }
}

2

Swift не позволяет вам переопределить переменную. stored propertyВместо этого вы можете использоватьcomputed property

class A {
    var property1 = "A: Stored Property 1"

    var property2: String {
        get {
            return "A: Computed Property 2"
        }
    }

    let property3 = "A: Constant Stored Property 3"

    //let can not be a computed property
    
    func foo() -> String {
        return "A: foo()"
    }
}

class B: A {

    //now it is a computed property
    override var property1: String {

        set { }
        get {
            return "B: overrode Stored Property 1"
        }
    }

    override var property2: String {
        get {
            return "B: overrode Computed Property 2"
        }
    }
    
    override func foo() -> String {
        return "B: foo()"
    }

    //let can not be overrode
}
func testPoly() {
    let a = A()
    
    XCTAssertEqual("A: Stored Property 1", a.property1)
    XCTAssertEqual("A: Computed Property 2", a.property2)
    
    XCTAssertEqual("A: foo()", a.foo())
    
    let b = B()
    XCTAssertEqual("B: overrode Stored Property 1", b.property1)
    XCTAssertEqual("B: overrode Computed Property 2", b.property2)
    
    XCTAssertEqual("B: foo()", b.foo())
    
    //B cast to A
    XCTAssertEqual("B: overrode Stored Property 1", (b as! A).property1)
    XCTAssertEqual("B: overrode Computed Property 2", (b as! A).property2)
    
    XCTAssertEqual("B: foo()", (b as! A).foo())
}

Это более очевидно при сравнении с Java, где поле класса не может быть переопределено и не поддерживает полиморфизм, потому что определяется во время компиляции (выполняется эффективно). Это называется скрытием переменной [О программе] Не рекомендуется использовать эту технику, потому что ее трудно читать / поддерживать

[Свойство Swift]


1

Вы также можете использовать функцию для переопределения. Это не прямой ответ, но может обогатить эту тему)

Класс А

override func viewDidLoad() {
    super.viewDidLoad()

    if shouldDoSmth() {
       // do
    }
}

public func shouldDoSmth() -> Bool {
    return true
}

Класс B: A

public func shouldDoSmth() -> Bool {
    return false
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.