Swift - método da classe que deve ser sobrescrito pela subclasse

91

Existe uma maneira padrão de fazer uma "função virtual pura" em Swift, ou seja. um que deve ser substituído por cada subclasse e que, se não for, causa um erro de tempo de compilação?

JuJoDi
fonte
Você poderia implementá-lo na superclasse e fazer uma afirmação. Eu vi isso usado em Obj-C, Java e Python.
David Skrundz
8
@NSArray Isso causa um erro de tempo de execução, e não de tempo de compilação
JuJoDi
Esta resposta também o ajudará. insira a descrição do link aqui
Chamath Jeevan
Uma função virtual pura é implementada por protocols (em comparação com interfaces em Java) Se você precisar usá-los como métodos abstratos, dê uma olhada nesta pergunta / resposta: stackoverflow.com/a/39038828/2435872
jboi

Respostas:

147

Você tem duas opções:

1. Use um protocolo

Defina a superclasse como um protocolo em vez de uma classe

Pro : verificar o tempo de compilação para ver se cada "subclasse" (não uma subclasse real) implementa os métodos necessários

Contra : A "superclasse" (protocolo) não pode implementar métodos ou propriedades

2. Afirmar na super versão do método

Exemplo:

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

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

Pro : pode implementar métodos e propriedades na superclasse

Contra : Sem verificação de tempo de compilação

Drewag
fonte
3
@jewirth você ainda não teria uma verificação de tempo de compilação nas subclasses
drewag
5
O protocolo não pode implementar métodos, mas você pode fornecê-los por meio de métodos de extensão.
David Moles
2
A partir do Swift 2.0, também existem extensões de protocolo :) Referência da Apple .
Ephemera
4
Embora fatalErrornão forneça verificação em tempo de compilação, é bom que o compilador seja pelo menos inteligente o suficiente para não exigir que você forneça um valor de retorno para o método quando o caminho de execução for chamado fatalError.
bugloaf
3
Caso 2: lembre-se do fato de que, se você chamar a super.someFunc()partir do método sobrescrito, receberá o erro apesar de tê-lo sobrescrito. Você sabe que não deve chamá-lo, mas outra pessoa não precisa saber disso e apenas seguir a prática padrão.
Jakub Truhlář
45

O seguinte permite herdar de uma classe e também ter a verificação do tempo de compilação do 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() { ... }

}
JMiguel
fonte
2
legal, typealias para o resgate :)
Chris Allinson
Há alguma maneira de impedir que os usuários desta API derivem suas classes clild de ViewControllerClass em vez de ViewController? Essa é uma ótima solução para mim porque estarei derivando do meu alias de tipo daqui a alguns anos e terei esquecido sobre quais funções precisam ser substituídas até então.
David Rector
@David Rector, você é capaz de tornar sua aula privada e suas typealias públicas? Desculpe, estou enviando mensagens do meu telefone, não consigo verificar.
ScottyBlades
1
Solução perfeita, obrigado por isso. Conforme sublinhado por @DavidRector, seria ótimo se houvesse uma solução para também fazer com que apenas as typealias fossem públicas, mas infelizmente não parece ser possível.
CyberDandy
35

Não há suporte para classes abstratas / funções virtuais, mas você provavelmente poderia usar um protocolo para a maioria dos casos:

protocol SomeProtocol {
    func someMethod()
}

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

Se SomeClass não implementar someMethod, você receberá este erro de tempo de compilação:

error: type 'SomeClass' does not conform to protocol 'SomeProtocol'
Connor
fonte
29
Observe que isso só funciona para a classe superior que implementa o protocolo. Qualquer subclasse pode ignorar alegremente os requisitos do protocolo.
memmons
2
Além disso, o uso de genéricos em protocolos não é suportado = (
Dielson Sales
14

Outra solução alternativa, se você não tiver muitos métodos "virtuais", é fazer com que a subclasse passe as "implementações" para o construtor da classe base como objetos de função:

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
fonte
1
não é tão útil se você tiver mais alguns métodos virtuais
Bushra Shahid
@ xs2bush Se mais de seus métodos são virtuais do que não, provavelmente é melhor declará-los em um protocolo e fornecer os 'não virtuais' por meio de métodos de extensão.
David Moles
1
isso é exatamente o que acabei fazendo
Bushra Shahid
0

Você pode usar protocolo versus asserção conforme sugerido na resposta aqui por drewag. No entanto, falta um exemplo para o protocolo. Estou cobrindo aqui,

Protocolo

protocol SomeProtocol {
    func someMethod()
}

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

Agora, todas as subclasses são necessárias para implementar o protocolo que é verificado em tempo de compilação. Se SomeClass não implementar someMethod, você receberá este erro de tempo de compilação:

erro: o tipo 'SomeClass' não está em conformidade com o protocolo 'SomeProtocol'

Observação: isso só funciona para a classe superior que implementa o protocolo. Qualquer subclasse pode ignorar alegremente os requisitos do protocolo. - como comentado pormemmons

Afirmação

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

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

No entanto, a asserção funcionará apenas em tempo de execução.

Sazzad Hissain Khan
fonte
-2

Sendo novo no desenvolvimento de iOS, não tenho certeza de quando isso foi implementado, mas uma maneira de obter o melhor dos dois mundos é implementar uma extensão para um 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
    }
}

A extensãoéo que lhe permite ter o valor padrão para uma função enquanto a função no protocolo regular ainda fornece um erro de tempo de compilação se não for definido

Jordânia
fonte
funções abstratas são o oposto de implementações padrão
Hogdotmac 01 de