Swift - méthode de classe qui doit être remplacée par la sous-classe

91

Existe-t-il un moyen standard de créer une "fonction virtuelle pure" dans Swift, ie. celui qui doit être remplacé par chaque sous-classe, et qui, si ce n'est pas le cas, provoque une erreur de compilation?

JuJoDi
la source
Vous pouvez l'implémenter dans la super classe et faire une assertion. J'ai vu cela utilisé dans Obj-C, Java et Python.
David Skrundz
8
@NSArray Cela provoque une erreur d'exécution et non de compilation
JuJoDi
Cette réponse vous aidera également. entrez la description du lien ici
Chamath Jeevan
Une fonction virtuelle pure est implémentée par protocols (par rapport à interfaces en Java) Si vous avez besoin de les utiliser comme des méthodes abstraites, regardez cette question / réponse: stackoverflow.com/a/39038828/2435872
jboi

Réponses:

147

Vous avez deux options:

1. Utilisez un protocole

Définir la superclasse comme un protocole au lieu d'une classe

Pro : vérification du temps de compilation pour savoir si chaque "sous-classe" (pas une sous-classe réelle) implémente la ou les méthodes requises

Inconvénient : la "superclasse" (protocole) ne peut pas implémenter de méthodes ou de propriétés

2. Affirmer dans la super version de la méthode

Exemple:

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

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

Pro : Peut implémenter des méthodes et des propriétés en superclasse

Inconvénient : pas de vérification du temps de compilation

dessiner
la source
3
@jewirth vous n'obtiendriez toujours pas une vérification du temps de compilation sur les sous
drewag
5
Le protocole ne peut pas implémenter de méthodes mais vous pouvez les fournir via des méthodes d'extension à la place.
David Moles
2
Depuis Swift 2.0, il existe maintenant des extensions de protocole :) Référence Apple .
Ephemera
4
Bien fatalErrorqu'il ne fournisse pas de vérification au moment de la compilation, il est bon que le compilateur soit au moins suffisamment intelligent pour ne pas vous obliger à fournir une valeur de retour pour la méthode lorsque le chemin d'exécution est appelé fatalError.
bugloaf
3
Cas 2: Attention au fait que si vous appelez à super.someFunc()partir de la méthode surchargée, vous obtenez l'erreur malgré le fait que vous l'avez remplacée. Vous savez que vous n'êtes pas censé l'appeler, mais quelqu'un d'autre n'a pas besoin de le savoir et de simplement suivre la pratique standard.
Jakub Truhlář
48

Ce qui suit permet d'hériter d'une classe et aussi de vérifier l'heure de compilation du protocole :)

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
la source
2
nice, typealias à la rescousse :)
Chris Allinson
Un moyen d'empêcher les utilisateurs de cette API de dériver leurs classes clild de ViewControllerClass au lieu de ViewController? C'est une excellente solution pour moi car je dériverai de mon alias de type dans quelques années et j'aurai oublié quelles fonctions doivent être remplacées d'ici là.
David Rector
@David Rector, pouvez-vous rendre votre classe privée et vos typealias publiques? Désolé, message depuis mon téléphone.
ScottyBlades
1
Solution parfaite, merci pour cela. Comme le souligne @DavidRector, ce serait formidable s'il y avait une solution pour faire en sorte que seules les typealias soient publiques, mais cela ne semble malheureusement pas possible.
CyberDandy
35

Il n'y a pas de support pour la classe abstraite / les fonctions virtuelles, mais vous pouvez probablement utiliser un protocole dans la plupart des cas:

protocol SomeProtocol {
    func someMethod()
}

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

Si SomeClass n'implémente pas someMethod, vous obtiendrez cette erreur de compilation:

error: type 'SomeClass' does not conform to protocol 'SomeProtocol'
Connor
la source
29
Notez que cela ne fonctionne que pour la classe la plus élevée qui implémente le protocole. Toutes les sous-classes peuvent ignorer allègrement les exigences du protocole.
memmons
2
De plus, l'utilisation de génériques sur les protocoles n'est pas prise en charge = (
Dielson Sales
14

Une autre solution de contournement, si vous n'avez pas trop de méthodes "virtuelles", consiste à demander à la sous-classe de passer les "implémentations" dans le constructeur de classe de base en tant qu'objets de fonction:

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
la source
1
pas si utile si vous avez quelques méthodes virtuelles supplémentaires
Bushra Shahid
@ xs2bush Si plus de vos méthodes sont virtuelles qu'autrement, vous feriez probablement mieux de les déclarer dans un protocole et de fournir les méthodes «non virtuelles» via des méthodes d'extension.
David Moles
1
c'est exactement ce que j'ai fini par faire
Bushra Shahid
0

Vous pouvez utiliser protocole vs assertion comme suggéré dans la réponse ici par drewag. Cependant, l'exemple du protocole est manquant. Je couvre ici,

Protocole

protocol SomeProtocol {
    func someMethod()
}

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

Désormais, toutes les sous-classes doivent implémenter le protocole qui est vérifié au moment de la compilation. Si SomeClass n'implémente pas someMethod, vous obtiendrez cette erreur de compilation:

erreur: le type 'SomeClass' n'est pas conforme au protocole 'SomeProtocol'

Remarque: cela ne fonctionne que pour la classe la plus élevée qui implémente le protocole. Toutes les sous-classes peuvent ignorer allègrement les exigences du protocole. - comme commenté parmemmons

Affirmation

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

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

Cependant, l'assertion ne fonctionnera que pendant l'exécution.

Sazzad Hissain Khan
la source
-2

Étant nouveau dans le développement iOS, je ne sais pas vraiment quand cela a été implémenté, mais une façon d'obtenir le meilleur des deux mondes est d'implémenter une extension pour un protocole:

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'extension est ce qui vous permet d'avoir la valeur par défaut pour une fonction tandis que la fonction dans le protocole régulier fournit toujours une erreur de compilation si elle n'est pas définie

Jordan
la source
les fonctions abstraites sont à l'opposé des implémentations par défaut
Hogdotmac