Swift - método de clase que debe ser anulado por subclase


91

¿Existe una forma estándar de hacer una "función virtual pura" en Swift, es decir. uno que debe ser anulado por cada subclase, y que, si no es así, causa un error de tiempo de compilación?


Podría implementarlo en la superclase y hacer una afirmación. He visto esto usado en Obj-C, Java y Python.
David Skrundz

8
@NSArray Esto causa un tiempo de ejecución, y no un tiempo de compilación, error
JuJoDi

Esta respuesta también te ayudará. ingrese la descripción del enlace aquí
Chamath Jeevan

protocolS implementa una función virtual pura (en comparación con interfaces en Java) Si necesita usarlos como métodos abstractos, consulte esta pregunta / respuesta: stackoverflow.com/a/39038828/2435872
jboi

Respuestas:


148

Tienes dos opciones:

1. Utilice un protocolo

Definir la superclase como Protocolo en lugar de Clase

Ventaja : compile la verificación de tiempo para ver si cada "subclase" (no una subclase real) implementa los métodos requeridos

Desventaja : la "superclase" (protocolo) no puede implementar métodos o propiedades

2. Afirmar en la super versión del método

Ejemplo:

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

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

Ventaja : puede implementar métodos y propiedades en superclase

Desventaja : Sin verificación de tiempo de compilación


3
@jewirth todavía no obtendría una verificación de tiempo de compilación en las subclases
drewag

5
El protocolo no puede implementar métodos, pero puede proporcionarlos a través de métodos de extensión.
David Moles

2
A partir de Swift 2.0, ahora también hay extensiones de protocolo :) Referencia de Apple .
Ephemera

4
Si bien fatalErrorno proporciona verificación en tiempo de compilación, es bueno que el compilador sea al menos lo suficientemente inteligente como para no requerir que proporciones un valor de retorno para el método cuando la ruta de ejecución llama fatalError.
bugloaf

3
Caso 2: Tenga en cuenta el hecho de que si llama super.someFunc()desde el método anulado, obtiene el error a pesar de que lo anuló. Sabes que se supone que no debes llamarlo, pero alguien más no tiene que saber eso y simplemente seguir la práctica estándar.
Jakub Truhlář

49

Lo siguiente permite heredar de una clase y también tener la verificación del tiempo de compilación del protocolo :)

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
nice, typealias to the rescue :)
Chris Allinson

¿Alguna forma de evitar que los usuarios de esta API deriven sus clases clild de ViewControllerClass en lugar de ViewController? Esta es una gran solución para mí porque derivaré de mi alias de tipo dentro de unos años y me habré olvidado de las funciones que deben anularse para entonces.
David Rector

@David Rector, ¿puedes hacer que tu clase sea privada y tus typealias públicas? Lo siento, enviando un mensaje desde mi teléfono, no puedo verificarlo
ScottyBlades

1
Solución perfecta, gracias por eso. Como lo subraya @DavidRector, sería genial si hubiera una solución para que también fuera público el tipo de letra, pero desafortunadamente no parece ser posible.
CyberDandy

35

No hay soporte para funciones virtuales / clases abstractas, pero probablemente podría usar un protocolo para la mayoría de los casos:

protocol SomeProtocol {
    func someMethod()
}

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

Si SomeClass no implementa someMethod, obtendrá este error de tiempo de compilación:

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

30
Tenga en cuenta que esto solo funciona para la clase superior que implementa el protocolo. Cualquier subclase puede ignorar alegremente los requisitos del protocolo.
memmons

2
Además, no se admite el uso de genéricos en protocolos = (
Dielson Sales

14

Otra solución, si no tiene demasiados métodos "virtuales", es hacer que la subclase pase las "implementaciones" al constructor de la clase base como objetos de función:

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
no es tan útil si tiene algunos métodos virtuales más
Bushra Shahid

@ xs2bush Si la mayoría de sus métodos son virtuales que no, probablemente sea mejor declararlos en un protocolo y proporcionar los 'no virtuales' a través de métodos de extensión.
David Moles

1
eso es exactamente lo que terminé haciendo
Bushra Shahid

0

Puede usar protocolo frente a aserción como se sugiere en la respuesta aquí de drewag. Sin embargo, falta un ejemplo del protocolo. Estoy cubriendo aquí,

Protocolo

protocol SomeProtocol {
    func someMethod()
}

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

Ahora se requiere que todas las subclases implementen el protocolo que se verifica en tiempo de compilación. Si SomeClass no implementa someMethod, obtendrá este error de tiempo de compilación:

error: el tipo 'SomeClass' no se ajusta al protocolo 'SomeProtocol'

Nota: esto solo funciona para la clase superior que implementa el protocolo. Cualquier subclase puede ignorar alegremente los requisitos del protocolo. - como se ha comentado pormemmons

Afirmación

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

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

Sin embargo, la aserción solo funcionará en tiempo de ejecución.


-2

Siendo nuevo en el desarrollo de iOS, no estoy del todo seguro de cuándo se implementó, pero una forma de obtener lo mejor de ambos mundos es implementar una extensión para un protocolo:

protocol ThingsToDo {
    func doThingOne()
}

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

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

La extensión es lo que le permite tener el valor predeterminado para una función, mientras que la función en el protocolo regular aún proporciona un error de tiempo de compilación si no está definida


1
las funciones abstractas son lo opuesto a las implementaciones predeterminadas
Hogdotmac
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.