web-dev-qa-db-fra.com

Cours abstraits en langue rapide

Existe-t-il un moyen de créer une classe abstraite dans le langage Swift ou s'agit-il d'une limitation tout comme Objective-C? J'aimerais créer une classe abstraite comparable à celle définie par Java en tant que classe abstraite.

128
kev

Il n'y a pas de classes abstraites dans Swift (juste comme Objective-C). Votre meilleur choix sera d'utiliser un Protocol , qui ressemble à une interface Java.

Avec Swift 2.0, vous pouvez ensuite ajouter des implémentations de méthodes et des propriétés calculées à l'aide d'extensions de protocole. Vos seules restrictions sont que vous ne pouvez pas fournir de variables membres ni de constantes et il n'y a pas de dispatch dynamique.

Un exemple de cette technique serait:

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

Notez que cela fournit des fonctionnalités de "classe abstraite" même pour les structures, mais les classes peuvent également implémenter le même protocole.

Notez également que chaque classe ou structure qui implémente le protocole Employee devra déclarer à nouveau la propriété annualSalary.

Surtout, remarquez que il n'y a pas de dispatch dynamique. Lorsque logSalary est appelé sur l'instance stockée en tant que SoftwareEngineer, il appelle la version remplacée de la méthode. Lorsque logSalary est appelé sur l'instance après avoir été converti en Employee, il appelle l'implémentation d'origine (il ne distribue pas de manière dynamique à la version remplacée, même si l'instance est en fait un Software Engineer.

Pour plus d'informations, consultez l'excellente vidéo WWDC sur cette fonctionnalité: Création de meilleures applications avec des types de valeur dans Swift

159
drewag

Notez que cette réponse est destinée à Swift 2.0 et supérieur

Vous pouvez obtenir le même comportement avec des protocoles et des extensions de protocole. 

Tout d'abord, vous écrivez un protocole qui sert d'interface pour toutes les méthodes devant être implémentées dans tous les types qui s'y conforment.

protocol Drivable {
    var speed: Float { get set }
}

Ensuite, vous pouvez ajouter un comportement par défaut à tous les types qui s'y conforment

extension Drivable {
    func accelerate(by: Float) {
        speed += by
    }
}

Vous pouvez maintenant créer de nouveaux types en implémentant Drivable.

struct Car: Drivable {
    var speed: Float = 0.0
    init() {}
}

let c = Car()
c.accelerate(10)

Donc, fondamentalement, vous obtenez:

  1. Contrôles de temps de compilation garantissant que toutes les Drivables implémentent speed
  2. Vous pouvez implémenter le comportement par défaut pour tous les types conformes à Drivable (accelerate)
  3. Drivable est garanti pour ne pas être instancié car c'est juste un protocole

En fait, ce modèle se comporte beaucoup plus comme des traits, ce qui signifie que vous pouvez vous conformer à plusieurs protocoles et utiliser leurs implémentations par défaut, alors qu'avec une superclasse abstraite, vous êtes limité à une simple hiérarchie de classes.

47
NSAddict

Je pense que ceci est le plus proche de la variable abstract de Java ou de la variable abstract de C #:

class AbstractClass {

    private init() {

    }
}

Notez que, pour que les modificateurs private fonctionnent, vous devez définir cette classe dans un fichier Swift séparé.

EDIT: Pourtant, ce code ne permet pas de déclarer une méthode abstraite et donc de forcer son implémentation.

14
Teejay

Le moyen le plus simple consiste à utiliser un appel à fatalError("Not Implemented") dans la méthode abstraite (non variable) de l'extension de protocole.

protocol MyInterface {
    func myMethod() -> String
}


extension MyInterface {

    func myMethod() -> String {
        fatalError("Not Implemented")
    }

}

class MyConcreteClass: MyInterface {

    func myMethod() -> String {
        return "The output"
    }

}

MyConcreteClass().myMethod()
8
Carlos García

Après avoir lutté pendant plusieurs semaines, j'ai enfin compris comment traduire une classe abstraite Java/PHP en 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());
    }
}

Cependant, je pense qu'Apple n'a pas implémenté les classes abstraites, car il utilise généralement le modèle de protocole délégué +. Par exemple, le même schéma ci-dessus serait mieux fait comme ceci: 

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());
        }
    }

J'avais besoin de ce type de modèle parce que je voulais partager certaines méthodes dans UITableViewController, telles que viewWillAppear, etc.

8
Josh Woodcock

Il existe un moyen de simuler des classes abstraites à l'aide de Protocoles . Voici un exemple:

protocol MyProtocol {
   func doIt()
}

class BaseClass {
    var myDelegate: MyProtocol!

    init() {
        ...
    }

    func myFunc() {
        ...
        self.myDelegate.doIt()
        ...
    }
}

class ChildClass: BaseClass, MyProtocol {
    override init(){
        super.init()
        self.myDelegate = self
    }

    func doIt() {
        // Custom implementation
    }
}
6
David Seca

Une autre façon de mettre en œuvre une classe abstraite consiste à bloquer l'initialiseur . Je l'ai fait de cette façon:

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")
        }
    }
}
1
Alexey Yarmolovich

Déplacez toutes les références aux propriétés abstraites et aux méthodes de la classe de base vers la mise en œuvre de l'extension de protocole, où contrainte auto vers la classe de base. Vous aurez accès à toutes les méthodes et propriétés de la classe Base. De plus, le compilateur vérifie la mise en œuvre des méthodes abstraites et des propriétés dans le protocole pour les classes dérivées

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;
  }
}
0
john07

Avec la limitation de pas de dispatch dynamique, vous pouvez faire quelque chose comme ceci:

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()
0
Christo Smal

J'essayais de créer une classe abstraite Weather, mais l'utilisation de protocoles n'était pas idéale car je devais écrire les mêmes méthodes init encore et encore. L'extension du protocole et l'écriture d'une méthode init avaient des problèmes, en particulier depuis que j'utilisais NSObject conformément à NSCoding.

Alors je suis arrivé avec ceci pour la conformité 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...
}        

En ce qui concerne init:

fileprivate init(param: Any...) {
    // Initialize
}
0
funct7