Cours abstraits en langage Swift

Existe-t-il un moyen de créer une classe abstraite dans le langage Swift, ou est-ce une limitation comme Objective-C? Je voudrais créer une classe abstraite comparable à ce que Java définit comme une classe abstraite.

122
demandé sur kev 2014-06-09 00:16:03

9 réponses

Il N'y a pas de classes abstraites dans Swift (tout comme Objective-C). Votre meilleur pari sera d'utiliser un protocole , qui ressemble à une Interface Java.

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

Un exemple de cette technique être:

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 type "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.

Remarquez surtout que il n'y a pas de répartition dynamique. Lorsque logSalary est appelée sur l'instance stockée en tant que SoftwareEngineer, elle appelle la version remplacée de la méthode. Quand logSalary est appelée sur l'instance après avoir été convertie en Employee, elle appelle l'implémentation d'origine (elle n'envoie pas dynamiquement à la version remplacée même si l'instance est en fait un Software Engineer.

Pour plus d'informations, consultez la grande vidéo WWDC sur cette fonctionnalité: construire de meilleures applications avec des Types de valeur dans Swift

156
répondu drewag 2016-08-31 15:00:32

notez que cette réponse est ciblée sur Swift 2.0 et supérieur

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

Tout d'abord, vous écrivez un protocole qui agit comme une interface pour toutes les méthodes qui doivent ê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 y sont conformes

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. vérifie le temps de compilation qui garantit que tous les Drivable s implémentent speed
  2. Vous pouvez implémenter un comportement par défaut pour tous les types conformes à Drivable (accelerate)
  3. Drivable est garanti de ne pas être instancié puisque c'est juste un protocole

Ce modèle se comporte en fait beaucoup plus comme des traits, ce qui signifie que vous pouvez vous conformer à plusieurs protocoles et prendre des implémentations par défaut de l'un d'entre eux, alors qu'avec une superclasse abstraite, vous êtes limité à une hiérarchie de classe simple.

46
répondu NSAddict 2015-07-10 12:55:47

Je pense que c'est le plus proche de Java abstract ou C # abstract:

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
répondu Teejay 2015-03-27 11:59:44

Après avoir lutté pendant plusieurs semaines, j'ai finalement réalisé 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é de classes abstraites car il utilise généralement le modèle delegate+protocol à la place. Par exemple, le même modèle 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 genre de modèle parce que je voulais commonize certaines méthodes dans UITableViewController telles que viewWillAppear etc. Était-ce utile?

8
répondu Josh Woodcock 2015-04-09 02:39:40

Le plus simple est d'utiliser un appel à fatalError("Not Implemented") dans la méthode abstraite (pas variable) sur 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
répondu Carlos García 2016-04-19 03:01:47

Il existe un moyen de simuler des classes abstraites en utilisant des 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
    }
}
5
répondu David Seca 2016-07-20 10:23:20

Une autre façon d'implémenter la classe abstraite est de 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
répondu Alexey Yarmolovich 2015-08-24 16:36:13

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 ont eu des problèmes, d'autant plus que j'utilisais NSObject conforme à NSCoding.

Donc je suis venu 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...
}        

Comme pour init:

fileprivate init(param: Any...) {
    // Initialize
}
0
répondu funct7 2017-02-05 14:10:45

Déplace toutes les références aux propriétés abstraites et aux méthodes de la classe de base vers l'implémentation de l'extension de protocole, où la contrainte D'Auto vers la classe de Base. Vous aurez accès à toutes les méthodes et propriétés de la classe de Base. En outre, le compilateur vérifie l'implémentation 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
répondu john07 2018-05-23 08:16:19