Есть ли в Swift стандартный способ сделать "чистую виртуальную функцию", т.е. тот, который должен быть переопределен каждым подклассом, и который, если это не так, вызывает ошибку времени компиляции?
Swift - метод класса, который должен быть переопределен подклассом
Ответы (8)
У вас есть два варианта:
1. Используйте протокол
Определите суперкласс как протокол вместо класса
Pro: во время компиляции проверяется, реализует ли каждый "подкласс" (а не фактический подкласс) требуемый метод (ы).
Минус: "суперкласс" (протокол) не может реализовывать методы или свойства.
2. Утвердить в супер версии метода
Пример:
class SuperClass {
func someFunc() {
fatalError("Must Override")
}
}
class Subclass : SuperClass {
override func someFunc() {
}
}
Pro: может реализовывать методы и свойства в суперклассе.
Недостаток: отсутствие проверки времени компиляции
fatalError
не обеспечивает проверку во время компиляции, приятно, что компилятор, по крайней мере, достаточно умен, чтобы не требовать от вас предоставления возвращаемого значения для метода, когда путь выполнения вызывает fatalError
.
- person bugloaf; 07.03.2017
super.someFunc()
из замещенного метода, вы получите ошибку, несмотря на то, что вы ее переопределили. Вы знаете, что вы не должны называть это, но кому-то еще не обязательно это знать и просто следовать стандартной практике.
- person Jakub Truhlář; 13.07.2018
Следующее позволяет наследовать от класса, а также проверять время компиляции протокола :)
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() { ... }
}
Нет никакой поддержки абстрактных классов / виртуальных функций, но вы, вероятно, можете использовать протокол в большинстве случаев:
protocol SomeProtocol {
func someMethod()
}
class SomeClass: SomeProtocol {
func someMethod() {}
}
Если SomeClass не реализует someMethod, вы получите эту ошибку времени компиляции:
error: type 'SomeClass' does not conform to protocol 'SomeProtocol'
Другой обходной путь, если у вас не слишком много «виртуальных» методов, состоит в том, чтобы подкласс передавал «реализации» в конструктор базового класса как объекты функции:
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)
}
}
Вы можете использовать протокол и утверждение, как это предлагается в здесь ответе 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() {
}
}
Однако утверждение будет работать только во время выполнения.
Это то, что я обычно делаю, чтобы вызвать ошибку времени компиляции:
class SuperClass {}
protocol SuperClassProtocol {
func someFunc()
}
typealias SuperClassType = SuperClass & SuperClassProtocol
class Subclass: SuperClassType {
func someFunc() {
// ...
}
}
Вы можете добиться этого, передав функцию в инициализатор.
Например
open class SuperClass {
private let abstractFunction: () -> Void
public init(abstractFunction: @escaping () -> Void) {
self.abstractFunction = abstractFunction
}
public func foo() {
// ...
abstractFunction()
}
}
public class SubClass: SuperClass {
public init() {
super.init(
abstractFunction: {
print("my implementation")
}
)
}
}
Вы можете расширить его, передав self в качестве параметра:
open class SuperClass {
private let abstractFunction: (SuperClass) -> Void
public init(abstractFunction: @escaping (SuperClass) -> Void) {
self.abstractFunction = abstractFunction
}
public func foo() {
// ...
abstractFunction(self)
}
}
public class SubClass: SuperClass {
public init() {
super.init(
abstractFunction: {
(_self: SuperClass) in
let _self: SubClass = _self as! SubClass
print("my implementation")
}
)
}
}
Pro:
- Проверка во время компиляции, реализует ли каждый подкласс требуемый метод (ы)
- Может реализовывать методы и свойства в суперклассе
- Обратите внимание, что вы не можете передать self в функцию, чтобы не получить утечку памяти.
Против:
- Это не самый красивый код
- Вы не можете использовать его для классов с
required init
Поскольку я новичок в разработке 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
}
}
Расширение - это то, что позволяет вам иметь значение по умолчанию для функции, в то время как функция в обычном протоколе по-прежнему выдает ошибку времени компиляции, если не определена.
protocol
s (по сравнению сinterface
s в Java). Если вам нужно использовать их как абстрактные методы, посмотрите этот вопрос / ответ: stackoverflow.com/a/39038828/2435872 - person jboi   schedule 19.08.2016