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

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

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

}

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

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

14 ответов

Решение

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

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

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 что имеет другое поведение?

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

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

Вошел на площадку этот код:

class Jedi {
    let lightsaberColor = "Blue"
}

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

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

не может переопределить неизменное свойство 'let' lightsaberColor'с помощью метода get'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 свойство не имеет смысла, потому что вы можете только изменить значение свойства.

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


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

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

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

class Jedi {
    var lightSaberColor = "Blue"
}


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

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

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

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

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

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

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

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

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

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

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


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

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]

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

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

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

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

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

Класс А

override func viewDidLoad() {
    super.viewDidLoad()

    if shouldDoSmth() {
       // do
    }
}

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

Класс Б: А

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

в дополнение к приведенным выше примерам,

      class SomeClass {
    var hello = "hello"
}
class ChildClass: SomeClass {
    override var hello: String {
        set {
            _hello = newValue
        }
        get {
            _hello
        }   
    }
    private var _hello: String = ""
}


// Testing...
let c = ChildClass()
c.hello = "test"
print(c.hello) // test
          //declaring class
class Games {
       var  no_player:Int = 0
        var name_game :String = ""
        var player_value:String {
            return "low"
        }
        func playing(){
            print( "the \(name_game) is played by \(no_player) players and the player value is \(player_value)")
        }
    }
//chess class inherits Games
    class Chess : Games {
        var location:String = ""
    }
//cricket class inherits Games
    class cricket : Games{
        var ball_brand = "sg"
    }
    //making object of Chess()
     let Chess1 = Chess()
    Chess1.name_game = "Chess "
    Chess1.no_player = 2
    Chess1.palying()
//polo inherits Games

    class polo:Games {
        override var player_value: String{
            return "high"
        }
        
        override func playing() {
            print("holla we are playing cricket with 10 members and the palyer value is  \(player_value) ")
        }
    }
    var polo1 = polo()
    polo1.playing()

Я использую этот подход:

      class Class {
    
    private lazy var _instance: AType = {
        return AType()
    } ()
    
    var instance: AType {
        return _instance
    }
    
}

class Subclass: Class {
    
    private lazy var _instance: AType = {
        return ASubType()
    } ()
    
    override var instance: AType {
        return _instance
    }
    
}

Вы можете использовать этот метод для обновления значений

class A {

      var lightSaberColor : String = "Red"

}

Вам не нужно переопределять переменные. Вы можете изменить значение переменной.

class B: A {* Изменить значение при инициализации класса.

       override init() {
    super.init()
      self.lightSaberColor = "Orange"
 }

/*
 * **Change Value when you will use this function.**
 */
func test() {
   lightSaberColor = "Green"
}

}

пусть b = B()

b.test ()

Другие вопросы по тегам