Swift - метод класса, который должен быть переопределен подклассом


91

Есть ли в Swift стандартный способ сделать "чистую виртуальную функцию", т.е. тот, который должен быть переопределен каждым подклассом, и который, если это не так, вызывает ошибку времени компиляции?


Вы можете реализовать это в суперклассе и сделать утверждение. Я видел, как это используется в Obj-C, Java и Python.
— Дэвид Скрундз

8
@NSArray Это вызывает ошибку времени выполнения, а не времени компиляции
— JuJoDi

Этот ответ вам тоже поможет. введите описание ссылки здесь
— Чамат Дживан

Чистая виртуальная функция реализуется с помощью protocols (по сравнению с interfaces в Java). Если вам нужно использовать их как абстрактные методы, посмотрите этот вопрос / ответ: stackoverflow.com/a/39038828/2435872
— jboi

Ответы:


153

У вас есть два варианта:

1. Используйте протокол

Определите суперкласс как протокол вместо класса

Pro : во время компиляции проверяется, реализует ли каждый "подкласс" (не фактический подкласс) требуемый метод (ы)

Против : "суперкласс" (протокол) не может реализовывать методы или свойства.

2. Утвердить в супер версии метода

Пример:

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

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

Pro : может реализовывать методы и свойства в суперклассе.

Con : нет проверки времени компиляции


3
@jewirth, у вас все равно не будет проверки времени компиляции подклассов
— drewag

6
Протокол не может реализовывать методы, но вместо этого вы можете предоставить их через методы расширения.
— Дэвид Моулс

2
Начиная с Swift 2.0 теперь есть и расширения протокола :) Ссылка на Apple .
— Ephemera

4
Хотя fatalErrorне обеспечивает проверку во время компиляции, приятно, что компилятор, по крайней мере, достаточно умен, чтобы не требовать от вас предоставления возвращаемого значения для метода при вызове пути выполнения fatalError.
— bugloaf

3
Случай 2: помните, что если вы вызываете super.someFunc()из переопределенного метода, вы получаете ошибку, несмотря на то, что вы ее переопределили. Вы знаете, что не должны называть это, но кому-то еще не обязательно это знать, и он просто должен следовать стандартной практике.
— Jakub Truhlář

50

Следующее позволяет наследовать от класса, а также проверять время компиляции протокола :)

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
хорошо, typealias на помощь :)
— Крис Аллинсон

Есть ли способ запретить пользователям этого API наследовать свои классы clild из ViewControllerClass, а не из ViewController? Это отличное решение для меня, потому что через несколько лет я буду извлекать из своего псевдонима типа и к тому времени уже забуду о том, какие функции нужно переопределить.
— Дэвид Ректор

@David Rector, можете ли вы сделать свой класс приватным, а ваш типалиас публичным? Извините, сообщение с моего телефона, не могу проверить себя.
— ScottyBlades 03

1
Прекрасное решение, спасибо за это. Как подчеркнуто @DavidRector, было бы здорово, если бы существовало решение сделать так, чтобы только typealias были общедоступными, но, к сожалению, это не представляется возможным.
— CyberDandy

35

Нет никакой поддержки абстрактных классов / виртуальных функций, но вы, вероятно, можете использовать протокол в большинстве случаев:

protocol SomeProtocol {
    func someMethod()
}

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

Если SomeClass не реализует someMethod, вы получите эту ошибку времени компиляции:

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

30
Обратите внимание, что это работает только для самого верхнего класса, реализующего протокол. Любые подклассы могут беспечно игнорировать требования протокола.
— меммонс

2
Также не поддерживается использование дженериков в протоколах = (
— Dielson Sales

14

Другой обходной путь, если у вас не слишком много «виртуальных» методов, состоит в том, чтобы подкласс передавал «реализации» в конструктор базового класса как объекты функции:

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
не очень полезно, если у вас есть еще несколько виртуальных методов
— Bushra Shahid

@ xs2bush Если у вас больше виртуальных методов, чем нет, вам, вероятно, лучше объявить их в протоколе и предоставить «невиртуальные» методы с помощью методов расширения.
— Дэвид Моулс

1
это именно то, чем я закончил
— Бушра Шахид

1

Вы можете использовать протокол против утверждения , как предложено в ответ здесь путем drewag. Однако пример протокола отсутствует. Я здесь прикрываюсь,

Протокол

protocol SomeProtocol {
    func someMethod()
}

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

Теперь все подклассы необходимы для реализации протокола, который проверяется во время компиляции. Если SomeClass не реализует someMethod, вы получите эту ошибку времени компиляции:

ошибка: тип SomeClass не соответствует протоколу SomeProtocol

Примечание: это работает только для самого верхнего класса, реализующего протокол. Любые подклассы могут беспечно игнорировать требования протокола. - так прокомментировал поmemmons

Утверждение

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

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

Однако утверждение будет работать только во время выполнения.


-2

Поскольку я новичок в разработке iOS, я не совсем уверен, когда это было реализовано, но один из способов получить лучшее из обоих миров - реализовать расширение для протокола:

protocol ThingsToDo {
    func doThingOne()
}

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

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

Расширение - это то, что позволяет вам иметь значение по умолчанию для функции, в то время как функция в обычном протоколе по-прежнему выдает ошибку времени компиляции, если не определена.


1
абстрактные функции противоположны реализациям по умолчанию
— Hogdotmac 01
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.