Перезапис збереженого ресурсу в 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"}

}

Чому мені не дозволяють надавати йому іншого значення?

Чому перевизначення збереженого майна є гидотою і робити це з обчисленою кошеркою? Що де вони думають?


1
можливий дублікат властивості Superciring Superclass з різним типом у Swift
Макс Маклеод

Відповіді:


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

55

Для мене ваш приклад не працює в Swift 3.0.1.

Я ввів на майданчик цей код:

class Jedi {
    let lightsaberColor = "Blue"
}

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

Вмикає помилку під час компіляції в Xcode:

не може змінити незмінний "нехай" властивість "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
те саме стосується коментаря вище
Max MacLeod

10

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

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


7

На жаль, цього, на жаль, неможливо зробити. Найкращою альтернативою є наступна:

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, де поле класу не можна переоцінити і не підтримує поліморфізм, тому що визначено під час компіляції (виконувати ефективно). Це називається прихованою змінною [About] Не рекомендується використовувати цю техніку, оскільки її важко читати / підтримувати

[Швидке майно]


1

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

Клас А

override func viewDidLoad() {
    super.viewDidLoad()

    if shouldDoSmth() {
       // do
    }
}

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

Клас В: А

public func shouldDoSmth() -> Bool {
    return false
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.