Mengganti properti yang disimpan di Swift

126

Saya perhatikan bahwa kompilator tidak akan membiarkan saya mengganti properti tersimpan dengan nilai tersimpan lain (yang tampak aneh):

class Jedi {
    var lightSaberColor = "Blue"
}


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

Namun, saya diizinkan melakukan ini dengan properti yang dihitung:

class Jedi {
    let lightSaberColor = "Blue"
}


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

}

Mengapa saya tidak diizinkan memberikan nilai lain?

Mengapa menimpa dengan properti yang disimpan adalah kekejian dan melakukannya dengan yang dihitung halal? Apa yang mereka pikirkan?

cfischer
sumber
1
kemungkinan duplikat dari properti superclass Overriding dengan tipe berbeda di Swift
Max MacLeod

Jawaban:

82

Mengapa saya tidak diizinkan untuk memberikan nilai lain?

Anda pasti diizinkan memberi properti warisan nilai yang berbeda. Anda dapat melakukannya jika Anda menginisialisasi properti dalam konstruktor yang mengambil nilai awal tersebut, dan meneruskan nilai yang berbeda dari kelas turunan:

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)

Mengganti properti tidak sama dengan memberinya nilai baru - ini lebih seperti memberi kelas properti yang berbeda. Faktanya, itulah yang terjadi ketika Anda mengganti properti yang dihitung: kode yang menghitung properti di kelas dasar diganti dengan kode yang menghitung penggantian untuk properti itu di kelas turunan.

[Apakah] mungkin untuk menimpa properti tersimpan yang sebenarnya, yaitu lightSaberColoryang memiliki beberapa perilaku lain?

Selain pengamat, properti yang disimpan tidak memiliki perilaku, jadi sebenarnya tidak ada yang perlu diganti. Memberikan properti nilai yang berbeda dimungkinkan melalui mekanisme yang dijelaskan di atas. Ini persis seperti yang coba dicapai contoh dalam pertanyaan, dengan sintaks yang berbeda.

dasblinkenlight
sumber
2
@MaxMacLeod Selain pengamat, properti yang disimpan tidak memiliki perilaku, jadi sebenarnya tidak ada yang perlu diganti. Dia ingin memberikan nilai yang berbeda pada properti tersimpan di subkelas tersebut, tetapi dia tidak yakin tentang mekanisme untuk mencapainya. Jawabannya menjelaskan bagaimana itu bisa dilakukan di Swift. Saya minta maaf atas balasan yang terlambat, tampaknya komentar Anda menyebabkan kebingungan yang cukup untuk menarik suara negatif, jadi saya memutuskan untuk menjelaskan apa yang terjadi.
dasblinkenlight
55

Bagi saya, contoh Anda tidak berfungsi di Swift 3.0.1.

Saya masukkan di taman bermain kode ini:

class Jedi {
    let lightsaberColor = "Blue"
}

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

Melempar kesalahan pada waktu kompilasi di Xcode:

tidak dapat menimpa properti 'let' property 'lightsaberColor' yang tidak dapat diubah dengan pengambil 'var'

Tidak, Anda tidak dapat mengubah jenis properti yang disimpan. Prinsip Substitusi Liskov memaksa Anda untuk mengizinkan subclass digunakan di tempat di mana superclass diinginkan.

Tetapi jika Anda mengubahnya menjadi vardan oleh karena itu menambahkan setdalam properti yang dihitung, Anda dapat mengganti properti yang disimpan dengan properti yang dihitung dari tipe yang sama.

class Jedi {
    var lightsaberColor = "Blue"
}


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

Hal ini dimungkinkan karena masuk akal untuk beralih dari properti tersimpan ke properti yang dihitung.

Tetapi mengganti varproperti yang disimpan dengan properti yang disimpan vartidak masuk akal, karena Anda hanya dapat mengubah nilai properti.

Namun, Anda tidak dapat mengganti properti tersimpan dengan properti tersimpan sama sekali.


Saya tidak akan mengatakan Sith adalah Jedi :-P. Oleh karena itu jelas bahwa ini tidak dapat bekerja.

Binarian
sumber
18
class SomeClass {
    var hello = "hello"
}
class ChildClass: SomeClass {
    override var hello: String {
        set {
            super.hello = newValue
        }
        get {
            return super.hello
        }    
    }
}
Zhiping Yang
sumber
13
Meskipun cuplikan kode ini dapat menyelesaikan pertanyaan, menyertakan penjelasan sangat membantu meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa mendatang, dan orang-orang itu mungkin tidak tahu alasan saran kode Anda.
DimaSan
15

Anda mungkin ingin memberikan nilai lain ke properti:

class Jedi {
    var lightSaberColor = "Blue"
}


class Sith: Jedi {
    override init() {
        super.init()
        self.lightSaberColor = "Red"
    }
}
zisoft
sumber
9
Hal yang sama berlaku sesuai komentar di atas
Max MacLeod
10

Untuk Swift 4, dari dokumentasi Apple :

Anda bisa mengganti instance yang diwariskan atau properti tipe untuk menyediakan pengambil dan penyetel kustom Anda sendiri untuk properti itu, atau untuk menambahkan pengamat properti untuk mengaktifkan properti pengganti untuk mengamati ketika nilai properti yang mendasarinya berubah.

riyasavla
sumber
7

Di Swift, sayangnya hal ini tidak mungkin dilakukan. Alternatif terbaik adalah sebagai berikut:

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


class Sith: Jedi {
    override var lightsaberColor : String {
        get {
            return "Red"
        }
    }
}
Noah Wilder
sumber
3

Saya memiliki masalah yang sama untuk menetapkan konstanta untuk pengontrol tampilan.

Karena saya menggunakan pembuat antarmuka untuk mengelola tampilan, saya tidak dapat menggunakan init(), jadi solusi saya mirip dengan jawaban lain, kecuali saya menggunakan variabel hitung hanya-baca pada kelas dasar dan kelas yang diwariskan.

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

class Sith: Jedi {
    override var type: String {
        get { return "Red" }
    }
}
Yassine ElBadaoui
sumber
3

Jika Anda mencoba melakukan itu di Swift 5, Anda akan disambut oleh a

Tidak dapat menimpa properti 'let' yang tidak dapat diubah 'lightSaberColor' dengan pengambil 'var'

Taruhan terbaik Anda adalah mendeklarasikannya sebagai properti yang dihitung.

Ini berfungsi karena kami hanya mengganti get {}fungsinya

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

class Red: Base {
   override var lightSaberColor: String { "red" }
}
Hugo Alonso
sumber
2

Swift tidak mengizinkan Anda mengganti variabel, stored propertybukan ini yang bisa Anda gunakancomputed 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())
}

Ini lebih jelas jika dibandingkan dengan Java, di mana bidang kelas tidak dapat ditimpa dan tidak mendukung polimorfisme karena ditentukan dalam waktu kompilasi (berjalan efisien). Disebut variabel bersembunyi [About] Tidak disarankan menggunakan teknik ini karena sulit dibaca / didukung

[Properti Swift]

yoAlex5
sumber
1

Anda juga dapat menggunakan fungsi untuk mengganti. Ini bukan jawaban langsung, tapi bisa memperkaya topik ini)

Kelas A

override func viewDidLoad() {
    super.viewDidLoad()

    if shouldDoSmth() {
       // do
    }
}

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

Kelas B: A

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