Swift - metodo di classe che deve essere sovrascritto dalla sottoclasse


91

Esiste un modo standard per creare una "funzione virtuale pura" in Swift, ad es. uno che deve essere sovrascritto da ogni sottoclasse e che, se non lo è, causa un errore in fase di compilazione?


Potresti implementarlo nella super classe e fare un'affermazione. L'ho visto usato in Obj-C, Java e Python.
David Skrundz

8
@NSArray Ciò causa un errore di runtime e non di compilazione
JuJoDi

Questa risposta aiuterà anche te. inserisci la descrizione del link qui
Chamath Jeevan

Una funzione virtuale pura è implementato da protocols (rispetto ai interfaces in Java) Se avete bisogno di utilizzare loro come metodi astratti sono sguardo a questa domanda / risposta: stackoverflow.com/a/39038828/2435872
jboi

Risposte:


148

Hai due opzioni:

1. Utilizzare un protocollo

Definisci la superclasse come protocollo invece che come classe

Pro : verifica del tempo di compilazione se ogni "sottoclasse" (non una sottoclasse effettiva) implementa i metodi richiesti

Contro : la "superclasse" (protocollo) non può implementare metodi o proprietà

2. Affermare nella versione super del metodo

Esempio:

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

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

Pro : può implementare metodi e proprietà in una superclasse

Contro : nessun controllo del tempo di compilazione


3
@jewirth non avresti ancora un controllo in fase di compilazione sulle sottoclassi
drewag

5
Il protocollo non può implementare metodi ma puoi fornirli tramite metodi di estensione.
David Moles

2
A partire da Swift 2.0 ora ci sono anche estensioni di protocollo :) Riferimento Apple .
Ephemera

4
Sebbene fatalErrornon fornisca il controllo in fase di compilazione, è bello che il compilatore sia almeno abbastanza intelligente da non richiedere di fornire un valore di ritorno per il metodo quando viene chiamato il percorso di esecuzione fatalError.
bugloaf

3
Caso 2: tieni presente il fatto che se chiami super.someFunc()dal metodo sovrascritto, ottieni l'errore nonostante tu lo abbia sovrascritto. Sai che non dovresti chiamarlo, ma qualcun altro non deve saperlo e segue semplicemente la pratica standard.
Jakub Truhlář

49

Quanto segue consente di ereditare da una classe e anche di controllare il tempo di compilazione del protocollo :)

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() { ... }

}

2
bello, tipealias in soccorso :)
Chris Allinson

Qualche modo per impedire agli utenti di questa API di derivare le loro classi clild da ViewControllerClass invece che da ViewController? Questa è un'ottima soluzione per me perché deriverò dal mio alias di tipo tra qualche anno e avrò dimenticato quali funzioni devono essere sovrascritte da allora.
David Rector

@David Rector, sei in grado di rendere la tua classe privata e le tue tipologie pubbliche? Messaggio spiacente dal mio telefono, non posso controllare da solo.
ScottyBlades

1
Soluzione perfetta, grazie per questo. Come sottolineato da @DavidRector, sarebbe fantastico se ci fosse una soluzione per fare in modo che anche solo il typealias fosse pubblico, ma sfortunatamente non sembra possibile.
CyberDandy

35

Non c'è alcun supporto per classi astratte / funzioni virtuali, ma probabilmente potresti usare un protocollo per la maggior parte dei casi:

protocol SomeProtocol {
    func someMethod()
}

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

Se SomeClass non implementa someMethod, riceverai questo errore in fase di compilazione:

error: type 'SomeClass' does not conform to protocol 'SomeProtocol'

30
Nota che funziona solo per la classe più in alto che implementa il protocollo. Qualsiasi sottoclasse può ignorare allegramente i requisiti del protocollo.
memmons

2
Inoltre, l'utilizzo di farmaci generici sui protocolli non è supportato = (
Dielson Sales

14

Un'altra soluzione alternativa, se non si hanno troppi metodi "virtuali", è fare in modo che la sottoclasse passi le "implementazioni" nel costruttore della classe base come oggetti funzione:

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

1
non così utile se hai qualche altro metodo virtuale
Bushra Shahid

@ xs2bush Se più metodi sono virtuali piuttosto che no, probabilmente faresti meglio a dichiararli in un protocollo e fornire quelli "non virtuali" tramite metodi di estensione.
David Moles

1
questo è esattamente quello che ho finito per fare
Bushra Shahid

0

Puoi usare protocollo vs asserzione come suggerito nella risposta qui da drewag. Tuttavia, manca un esempio per il protocollo. Sto coprendo qui,

Protocollo

protocol SomeProtocol {
    func someMethod()
}

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

Ora ogni sottoclasse è richiesta per implementare il protocollo che viene controllato in fase di compilazione. Se SomeClass non implementa someMethod, riceverai questo errore in fase di compilazione:

errore: il tipo "SomeClass" non è conforme al protocollo "SomeProtocol"

Nota: funziona solo per la classe più in alto che implementa il protocollo. Qualsiasi sottoclasse può ignorare allegramente i requisiti del protocollo. - come commentato damemmons

Asserzione

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

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

Tuttavia, l'asserzione funzionerà solo in runtime.


-2

Essendo nuovo allo sviluppo iOS, non sono del tutto sicuro di quando questo è stato implementato, ma un modo per ottenere il meglio da entrambi i mondi è implementare un'estensione per un protocollo:

protocol ThingsToDo {
    func doThingOne()
}

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

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

L'estensioneèquello che ti permette di avere il valore predefinito per una funzione mentre la funzione nel protocollo normale fornisce ancora un errore in fase di compilazione se non definita


1
le funzioni astratte sono l'opposto delle implementazioni predefinite
Hogdotmac
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.