C'è un modo per creare una classe astratta in Swift Language, o è una limitazione proprio come Objective-C? Vorrei creare una classe astratta paragonabile a quella che Java definisce una classe astratta.
C'è un modo per creare una classe astratta in Swift Language, o è una limitazione proprio come Objective-C? Vorrei creare una classe astratta paragonabile a quella che Java definisce una classe astratta.
Risposte:
Non ci sono classi astratte in Swift (proprio come Objective-C). La tua scommessa migliore sarà quella di utilizzare un protocollo , che è come un'interfaccia Java.
Con Swift 2.0, è quindi possibile aggiungere implementazioni di metodi e implementazioni di proprietà calcolate utilizzando le estensioni di protocollo. Le tue uniche restrizioni sono che non puoi fornire variabili o costanti membri e non c'è invio dinamico .
Un esempio di questa tecnica sarebbe:
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
Si noti che ciò fornisce funzionalità di "classe astratta" anche per le strutture, ma le classi possono anche implementare lo stesso protocollo.
Si noti inoltre che ogni classe o struttura che implementa il protocollo Employee dovrà dichiarare nuovamente la proprietà annualSalary.
Soprattutto, si noti che non vi è alcuna spedizione dinamica . Quando logSalary
viene chiamato sull'istanza memorizzata come una SoftwareEngineer
, chiama la versione sovrascritta del metodo. Quando logSalary
viene chiamato sull'istanza dopo che è stato Employee
eseguito il cast su un , chiama l'implementazione originale (non invia dinamicamente alla versione sovrascritta anche se l'istanza è in realtà a Software Engineer
.
Per ulteriori informazioni, guarda i fantastici video del WWDC su quella funzione: Creazione di app migliori con tipi di valore in Swift
protocol Animal { var property : Int { get set } }
. Puoi anche lasciare il set se non vuoi che la proprietà abbia un setter
func logSalary()
alla dichiarazione del protocollo Employee, l'esempio viene stampato overridden
per entrambe le chiamate a logSalary()
. Questo è in Swift 3.1. In questo modo ottieni i benefici del polimorfismo. Viene chiamato il metodo corretto in entrambi i casi.
Nota che questa risposta è indirizzata a Swift 2.0 e versioni successive
È possibile ottenere lo stesso comportamento con protocolli ed estensioni di protocollo.
Innanzitutto, scrivi un protocollo che funge da interfaccia per tutti i metodi che devono essere implementati in tutti i tipi conformi ad esso.
protocol Drivable {
var speed: Float { get set }
}
Quindi è possibile aggiungere un comportamento predefinito a tutti i tipi conformi ad esso
extension Drivable {
func accelerate(by: Float) {
speed += by
}
}
Ora puoi creare nuovi tipi implementando Drivable
.
struct Car: Drivable {
var speed: Float = 0.0
init() {}
}
let c = Car()
c.accelerate(10)
Quindi in pratica ottieni:
Drivable
implementinospeed
Drivable
( accelerate
)Drivable
è garantito per non essere istanziato poiché è solo un protocolloQuesto modello in realtà si comporta in modo molto più simile ai tratti, il che significa che puoi conformarti a più protocolli e assumere implementazioni predefinite di ognuno di essi, mentre con una superclasse astratta sei limitato a una semplice gerarchia di classi.
UICollectionViewDatasource
. Vorrei rimuovere tutto il boilerplate e incapsularlo in un protocollo / estensione separato e riutilizzarlo da più classi. In effetti, il modello di modello sarebbe perfetto qui, ma ...
Penso che questo sia il più vicino a Java abstract
o C # abstract
:
class AbstractClass {
private init() {
}
}
Si noti che, affinché i private
modificatori funzionino, è necessario definire questa classe in un file Swift separato.
EDIT: Tuttavia, questo codice non consente di dichiarare un metodo astratto e quindi forzarne l'implementazione.
Il modo più semplice è utilizzare una chiamata al fatalError("Not Implemented")
metodo astratto (non variabile) sull'estensione del protocollo.
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()
ma funziona! La chiave è inclusa myMethod
nella dichiarazione del protocollo; altrimenti la chiamata si arresta in modo anomalo.
Dopo aver lottato per diverse settimane, ho finalmente capito come tradurre una classe astratta Java / PHP in 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());
}
}
Tuttavia, penso che Apple non abbia implementato le classi astratte perché in genere utilizza invece il modello delegate + protocol. Ad esempio lo stesso modello sopra sarebbe meglio fare così:
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());
}
}
Avevo bisogno di questo tipo di modello perché volevo rendere comuni alcuni metodi in UITableViewController come viewWillAppear ecc. Ti è stato utile?
Esiste un modo per simulare le classi astratte utilizzando i protocolli. Questo è un esempio:
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
}
}
Un altro modo per implementare la classe astratta è bloccare l'inizializzatore. L'ho fatto in questo modo:
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")
}
}
}
Stavo cercando di creare una Weather
classe astratta, ma usare i protocolli non era l'ideale poiché dovevo scrivere gli stessi init
metodi più e più volte. L'estensione del protocollo e la scrittura di un init
metodo avevano i suoi problemi, soprattutto da quando stavo usando NSObject
conformeNSCoding
.
Quindi ho pensato a questo per la NSCoding
conformità:
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...
}
Per quanto riguarda init
:
fileprivate init(param: Any...) {
// Initialize
}
Spostare tutti i riferimenti a proprietà e metodi astratti della classe Base sull'implementazione dell'estensione del protocollo, dove Auto-vincolo sulla classe Base. Avrai accesso a tutti i metodi e le proprietà della classe Base. Inoltre, il compilatore controlla l'implementazione di metodi e proprietà astratti nel protocollo per le classi derivate
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;
}
}
Con la limitazione di nessuna spedizione dinamica, potresti fare qualcosa del genere:
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()