Swift - metode kelas yang harus diganti oleh subkelas

91

Apakah ada cara standar untuk membuat "fungsi virtual murni" di Swift, yaitu. salah satu yang harus diganti oleh setiap subclass, dan yang mana, jika tidak, menyebabkan kesalahan waktu kompilasi?

JuJoDi
sumber
Anda dapat menerapkannya di kelas super dan membuat pernyataan. Saya telah melihat ini digunakan di Obj-C, Java dan Python.
David Skrundz
8
@NSArray Ini menyebabkan runtime, dan bukan waktu kompilasi, kesalahan
JuJoDi
Jawaban ini akan membantu Anda juga. masukkan deskripsi tautan di sini
Chamath Jeevan
Fungsi virtual murni diimplementasikan oleh protocols (dibandingkan dengan interfaces di Java) Jika Anda perlu menggunakannya seperti metode abstrak, lihat pertanyaan / jawaban ini: stackoverflow.com/a/39038828/2435872
jboi

Jawaban:

153

Anda memiliki dua pilihan:

1. Gunakan Protokol

Tentukan superclass sebagai Protokol, bukan sebagai Kelas

Pro : Pemeriksaan waktu kompilasi apakah setiap "subclass" (bukan subclass sebenarnya) mengimplementasikan metode yang diperlukan

Kontra : "superclass" (protokol) tidak dapat mengimplementasikan metode atau properti

2. Tegaskan dalam versi super dari metode ini

Contoh:

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

Pro : Dapat mengimplementasikan metode dan properti di superclass

Kontra : Tidak ada pemeriksaan waktu kompilasi

menggambar
sumber
3
@jewirth Anda masih tidak akan mendapatkan pemeriksaan waktu kompilasi pada subclass
drewag
6
Protokol tidak dapat mengimplementasikan metode tetapi Anda dapat menyediakannya melalui metode ekstensi.
David Moles
2
Mulai Swift 2.0 sekarang ada juga ekstensi protokol :) Referensi Apple .
Efemera
4
Meskipun fatalErrortidak menyediakan pemeriksaan waktu kompilasi, sebaiknya kompilator setidaknya cukup pintar untuk tidak meminta Anda memberikan nilai kembalian untuk metode saat jalur eksekusi memanggil fatalError.
bugloaf
3
Kasus 2: Ingat fakta, bahwa jika Anda memanggil super.someFunc()dari metode yang ditimpa, Anda mendapatkan kesalahan meskipun Anda menimpanya. Anda tahu bahwa Anda tidak seharusnya menyebutnya, tetapi orang lain tidak harus mengetahuinya dan hanya mengikuti praktik standar.
Jakub Truhlář
50

Hal berikut memungkinkan untuk mewarisi dari kelas dan juga memiliki pemeriksaan waktu kompilasi protokol :)

protocol ViewControllerProtocol {
    func setupViews()
    func setupConstraints()
}

typealias ViewController = ViewControllerClass & ViewControllerProtocol

class ViewControllerClass : UIViewController {

    override func viewDidLoad() {
        self.setup()
    }

    func setup() {
        guard let controller = self as? ViewController else {
            return
        }

        controller.setupViews()
        controller.setupConstraints()
    }

    //.... and implement methods related to UIViewController at will

}

class SubClass : ViewController {

    //-- in case these aren't here... an error will be presented
    func setupViews() { ... }
    func setupConstraints() { ... }

}
JMiguel
sumber
2
bagus, typealias untuk menyelamatkan :)
Chris Allinson
Adakah cara untuk menghentikan pengguna API ini agar tidak mendapatkan kelas clild mereka dari ViewControllerClass daripada dari ViewController? Ini adalah solusi yang bagus untuk saya karena saya akan mengambil dari alias tipe saya beberapa tahun dari sekarang dan akan lupa tentang fungsi apa yang perlu diganti saat itu.
David Rector
@David Rector, apakah Anda dapat menjadikan kelas Anda privat dan tipe Anda menjadi publik? Maaf mengirim pesan dari ponsel saya, tidak dapat memeriksa diri sendiri.
ScottyBlades
1
Solusi sempurna, terima kasih untuk itu. Seperti yang digarisbawahi oleh @DavidRector, alangkah baiknya jika ada solusi untuk juga membuatnya jadi hanya typealias yang publik, tetapi sayangnya hal itu tampaknya tidak memungkinkan.
CyberDandy
35

Tidak ada dukungan untuk kelas abstrak / fungsi virtual, tetapi Anda mungkin dapat menggunakan protokol untuk banyak kasus:

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

Jika SomeClass tidak mengimplementasikan someMethod, Anda akan mendapatkan error waktu kompilasi ini:

error: type 'SomeClass' does not conform to protocol 'SomeProtocol'
Connor
sumber
30
Perhatikan ini hanya bekerja untuk kelas paling atas yang mengimplementasikan protokol. Setiap subclass dapat mengabaikan persyaratan protokol.
memmons
2
Selain itu, penggunaan obat generik pada protokol tidak didukung = (
Dielson Sales
14

Solusi lain, jika Anda tidak memiliki terlalu banyak metode "virtual", adalah membuat subclass meneruskan "implementasi" ke konstruktor kelas dasar sebagai objek fungsi:

class MyVirtual {

    // 'Implementation' provided by subclass
    let fooImpl: (() -> String)

    // Delegates to 'implementation' provided by subclass
    func foo() -> String {
        return fooImpl()
    }

    init(fooImpl: (() -> String)) {
        self.fooImpl = fooImpl
    }
}

class MyImpl: MyVirtual {

    // 'Implementation' for super.foo()
    func myFoo() -> String {
        return "I am foo"
    }

    init() {
        // pass the 'implementation' to the superclass
        super.init(myFoo)
    }
}
David Moles
sumber
1
tidak begitu berguna jika Anda memiliki beberapa metode virtual lagi
Bushra Shahid
@ xs2bush Jika lebih banyak metode Anda yang virtual daripada tidak, Anda mungkin lebih baik mendeklarasikannya dalam protokol, dan menyediakan metode 'non-virtual' melalui metode ekstensi.
David Moles
1
itulah yang akhirnya saya lakukan
Bushra Shahid
1

Anda dapat menggunakan protokol vs pernyataan seperti yang disarankan dalam jawaban di sini oleh drewag. Namun, contoh untuk protokolnya hilang. Saya menutupi di sini,

Protokol

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

Sekarang setiap subclass diharuskan mengimplementasikan protokol yang diperiksa dalam waktu kompilasi. Jika SomeClass tidak mengimplementasikan someMethod, Anda akan mendapatkan error waktu kompilasi ini:

kesalahan: ketik 'SomeClass' tidak sesuai dengan protokol 'SomeProtocol'

Catatan: ini hanya berfungsi untuk kelas paling atas yang mengimplementasikan protokol. Setiap subclass dapat mengabaikan persyaratan protokol. - seperti yang dikomentari olehmemmons

Tuntutan

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

Namun, pernyataan hanya akan berfungsi pada waktu proses.

Sazzad Hissain Khan
sumber
-2

Karena baru dalam pengembangan iOS, saya tidak sepenuhnya yakin kapan ini diterapkan, tetapi salah satu cara untuk mendapatkan yang terbaik dari kedua dunia adalah dengan menerapkan ekstensi untuk protokol:

protocol ThingsToDo {
    func doThingOne()
}

extension ThingsToDo {
    func doThingTwo() { /* Define code here */}
}

class Person: ThingsToDo {
    func doThingOne() {
        // Already defined in extension
        doThingTwo()
        // Rest of code
    }
}

Ekstensi inilah yang memungkinkan Anda memiliki nilai default untuk suatu fungsi sementara fungsi dalam protokol reguler masih memberikan kesalahan waktu kompilasi jika tidak ditentukan

Yordania
sumber
1
fungsi abstrak adalah kebalikan dari implementasi default
Hogdotmac