Чи є спосіб створити абстрактний клас у Swift Language, чи це обмеження так само, як Objective-C? Я хотів би створити абстрактний клас, порівнянний з тим, що Java визначає як абстрактний клас.
Чи є спосіб створити абстрактний клас у Swift Language, чи це обмеження так само, як Objective-C? Я хотів би створити абстрактний клас, порівнянний з тим, що Java визначає як абстрактний клас.
Відповіді:
У Swift немає абстрактних класів (як і у Objective-C). Вашою найкращою ставкою буде використання протоколу , який схожий на інтерфейс Java.
За допомогою Swift 2.0 ви можете додавати реалізацію методу та обчислювати реалізацію властивостей за допомогою розширень протоколу. Ваші єдині обмеження полягають у тому, що ви не можете надати змінних чи констант-членів і немає динамічної відправки .
Прикладом цієї методики може бути:
protocol Employee {
var annualSalary: Int {get}
}
extension Employee {
var biweeklySalary: Int {
return self.annualSalary / 26
}
func logSalary() {
print("$\(self.annualSalary) per year or $\(self.biweeklySalary) biweekly")
}
}
struct SoftwareEngineer: Employee {
var annualSalary: Int
func logSalary() {
print("overridden")
}
}
let sarah = SoftwareEngineer(annualSalary: 100000)
sarah.logSalary() // prints: overridden
(sarah as Employee).logSalary() // prints: $100000 per year or $3846 biweekly
Зауважте, що це надає "абстрактний клас", подібний до функцій навіть для структур, але класи також можуть реалізувати той самий протокол.
Також зауважте, що кожному класу чи структурі, яка реалізує протокол Employee, доведеться знову оголосити властивість щорічної заробітної плати.
Найголовніше, зауважте, що немає динамічної відправки . Коли logSalary
викликається екземпляр, який зберігається як a, SoftwareEngineer
він викликає перезаписану версію методу. Коли logSalary
викликається екземпляр після того, як він був переданий до Employee
, він викликає оригінальну реалізацію (вона не динамічно передається до перекритої версії, хоча екземпляр насправді є Software Engineer
.
Для отримання додаткової інформації перегляньте чудове відео WWDC про цю особливість: Створення кращих програм із типовими значеннями в Swift
protocol Animal { var property : Int { get set } }
. Ви також можете пропустити цей набір, якщо ви не хочете, щоб у
func logSalary()
до декларації протоколу працівника, приклад друкує overridden
для обох дзвінків до logSalary()
. Це в Swift 3.1. Таким чином ви отримуєте переваги поліморфізму. Правильний метод називається в обох випадках.
Зауважте, що ця відповідь націлена на Swift 2.0 і вище
Ви можете досягти такої ж поведінки з протоколами та розширеннями протоколів.
По-перше, ви пишете протокол, який виконує функції інтерфейсу для всіх методів, які мають бути реалізовані у всіх типах, що відповідають йому.
protocol Drivable {
var speed: Float { get set }
}
Потім ви можете додати поведінку за замовчуванням до всіх типів, які відповідають їй
extension Drivable {
func accelerate(by: Float) {
speed += by
}
}
Тепер ви можете створювати нові типи, впроваджуючи Drivable
.
struct Car: Drivable {
var speed: Float = 0.0
init() {}
}
let c = Car()
c.accelerate(10)
Отже, ви отримуєте:
Drivable
sspeed
Drivable
( accelerate
)Drivable
гарантується, що не буде моментувати, оскільки це просто протоколЦя модель насправді поводиться набагато більше, як риси, тобто ви можете відповідати декільком протоколам і приймати за замовчуванням реалізацію будь-якого з них, тоді як з абстрактним суперкласом ви обмежені простою ієрархією класів.
UICollectionViewDatasource
. Я хотів би зняти всю котлову та інкапсулювати її в окремий протокол / розширення, а потім повторно використовувати декілька класів. Насправді, шаблон шаблону був би тут ідеальним, але ...
Я думаю, що це найближче до Java abstract
або C # abstract
:
class AbstractClass {
private init() {
}
}
Зауважте, що для роботи private
модифікаторів ви повинні визначити цей клас в окремому файлі Swift.
EDIT: Тим не менш, цей код не дозволяє оголосити абстрактний метод і, таким чином, змусити його здійснити.
Найпростіший спосіб - використовувати виклик до fatalError("Not Implemented")
абстрактного методу (не змінного) на розширенні протоколу.
protocol MyInterface {
func myMethod() -> String
}
extension MyInterface {
func myMethod() -> String {
fatalError("Not Implemented")
}
}
class MyConcreteClass: MyInterface {
func myMethod() -> String {
return "The output"
}
}
MyConcreteClass().myMethod()
(MyConcreteClass() as MyInterface).myMethod()
але це так! Ключ включається myMethod
в декларацію протоколу; інакше виклик завершується.
Після того, як я боровся кілька тижнів, я нарешті зрозумів, як перекласти абстрактний клас Java / PHP на Swift:
public class AbstractClass: NSObject {
internal override init(){}
public func getFoodToEat()->String
{
if(self._iAmHungry())
{
return self._myFavoriteFood();
}else{
return "";
}
}
private func _myFavoriteFood()->String
{
return "Sandwich";
}
internal func _iAmHungry()->Bool
{
fatalError(__FUNCTION__ + "Must be overridden");
return false;
}
}
public class ConcreteClass: AbstractClass, IConcreteClass {
private var _hungry: Bool = false;
public override init() {
super.init();
}
public func starve()->Void
{
self._hungry = true;
}
public override func _iAmHungry()->Bool
{
return self._hungry;
}
}
public protocol IConcreteClass
{
func _iAmHungry()->Bool;
}
class ConcreteClassTest: XCTestCase {
func testExample() {
var concreteClass: ConcreteClass = ConcreteClass();
XCTAssertEqual("", concreteClass.getFoodToEat());
concreteClass.starve();
XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
}
}
Однак я думаю, що Apple не реалізувала абстрактні класи, тому що зазвичай використовує схему протоколів delegate +. Наприклад, таку ж схему вище було б краще зробити так:
import UIKit
public class GoldenSpoonChild
{
private var delegate: IStomach!;
internal init(){}
internal func setup(delegate: IStomach)
{
self.delegate = delegate;
}
public func getFoodToEat()->String
{
if(self.delegate.iAmHungry())
{
return self._myFavoriteFood();
}else{
return "";
}
}
private func _myFavoriteFood()->String
{
return "Sandwich";
}
}
public class Mother: GoldenSpoonChild, IStomach
{
private var _hungry: Bool = false;
public override init()
{
super.init();
super.setup(self);
}
public func makeFamilyHungry()->Void
{
self._hungry = true;
}
public func iAmHungry()->Bool
{
return self._hungry;
}
}
protocol IStomach
{
func iAmHungry()->Bool;
}
class DelegateTest: XCTestCase {
func testGetFood() {
var concreteClass: Mother = Mother();
XCTAssertEqual("", concreteClass.getFoodToEat());
concreteClass.makeFamilyHungry();
XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
}
}
Мені потрібен такий зразок, тому що я хотів поєднати деякі методи в UITableViewController, такі як viewWillAppear тощо. Чи корисно це було?
Існує спосіб моделювання абстрактних класів за допомогою протоколів. Це приклад:
protocol MyProtocol {
func doIt()
}
class BaseClass {
weak var myDelegate: MyProtocol?
init() {
...
}
func myFunc() {
...
self.myDelegate?.doIt()
...
}
}
class ChildClass: BaseClass, MyProtocol {
override init(){
super.init()
self.myDelegate = self
}
func doIt() {
// Custom implementation
}
}
Ще один спосіб, як можна реалізувати абстрактний клас, - це блокувати ініціалізатор. Я зробив це так:
class Element:CALayer { // IT'S ABSTRACT CLASS
override init(){
super.init()
if self.dynamicType === Element.self {
fatalError("Element is abstract class, do not try to create instance of this class")
}
}
}
Я намагався зробити Weather
абстрактний клас, але використання протоколів було не ідеальним, оскільки мені довелося писати одні й ті ж init
методи знову і знову. Розширення протоколу та написання init
методу мали свої проблеми, тим більше, що я використовував NSObject
відповідність NSCoding
.
Тому я придумав це для NSCoding
відповідності:
required init?(coder aDecoder: NSCoder) {
guard type(of: self) != Weather.self else {
fatalError("<Weather> This is an abstract class. Use a subclass of `Weather`.")
}
// Initialize...
}
Що стосується init
:
fileprivate init(param: Any...) {
// Initialize
}
Перемістіть усі посилання на абстрактні властивості та методи класу Base до реалізації розширення протоколу, де самообмеження до класу Base. Ви отримаєте доступ до всіх методів та властивостей класу Base. Додатково компілятор перевіряє реалізацію абстрактних методів та властивостей у протоколі для похідних класів
protocol Commom:class{
var tableView:UITableView {get};
func update();
}
class Base{
var total:Int = 0;
}
extension Common where Self:Base{
func update(){
total += 1;
tableView.reloadData();
}
}
class Derived:Base,Common{
var tableView:UITableView{
return owner.tableView;
}
}
Обмежуючи відсутність динамічної відправки, ви можете зробити щось подібне:
import Foundation
protocol foo {
static var instance: foo? { get }
func prt()
}
extension foo {
func prt() {
if Thread.callStackSymbols.count > 30 {
print("super")
} else {
Self.instance?.prt()
}
}
}
class foo1 : foo {
static var instance : foo? = nil
init() {
foo1.instance = self
}
func prt() {
print("foo1")
}
}
class foo2 : foo {
static var instance : foo? = nil
init() {
foo2.instance = self
}
func prt() {
print("foo2")
}
}
class foo3 : foo {
static var instance : foo? = nil
init() {
foo3.instance = self
}
}
var f1 : foo = foo1()
f1.prt()
var f2 : foo = foo2()
f2.prt()
var f3 : foo = foo3()
f3.prt()