¿Hay alguna manera de crear una clase abstracta en el lenguaje Swift o es una limitación al igual que Objective-C? Me gustaría crear una clase abstracta comparable a lo que Java define como una clase abstracta.
¿Hay alguna manera de crear una clase abstracta en el lenguaje Swift o es una limitación al igual que Objective-C? Me gustaría crear una clase abstracta comparable a lo que Java define como una clase abstracta.
Respuestas:
No hay clases abstractas en Swift (al igual que Objective-C). Su mejor opción será utilizar un protocolo , que es como una interfaz Java.
Con Swift 2.0, puede agregar implementaciones de métodos e implementaciones de propiedades calculadas utilizando extensiones de protocolo. Sus únicas restricciones son que no puede proporcionar variables miembro o constantes y no hay despacho dinámico .
Un ejemplo de esta técnica sería:
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
Tenga en cuenta que esto proporciona características de "clase abstracta" incluso para estructuras, pero las clases también pueden implementar el mismo protocolo.
Observe también que cada clase o estructura que implemente el protocolo del Empleado deberá declarar nuevamente la propiedad del Salario anual.
Lo más importante, tenga en cuenta que no hay despacho dinámico . Cuando logSalary
se llama en la instancia que se almacena como SoftwareEngineer
, llama a la versión anulada del método. Cuando se invoca logSalary
en la instancia después de que se ha convertido en un Employee
, llama a la implementación original (no se envía dinámicamente a la versión anulada, aunque la instancia es realmente un Software Engineer
.
Para obtener más información, consulte el excelente video de WWDC sobre esa característica: Crear mejores aplicaciones con tipos de valor en Swift
protocol Animal { var property : Int { get set } }
. También puede omitir el conjunto si no desea que la propiedad tenga un setter
func logSalary()
a la declaración de protocolo de empleado, el ejemplo se imprime overridden
para ambas llamadas a logSalary()
. Esto está en Swift 3.1. Así obtienes los beneficios del polimorfismo. Se llama al método correcto en ambos casos.
Tenga en cuenta que esta respuesta está dirigida a Swift 2.0 y superior
Puede lograr el mismo comportamiento con protocolos y extensiones de protocolo.
Primero, escribe un protocolo que actúa como una interfaz para todos los métodos que deben implementarse en todos los tipos que se ajustan a él.
protocol Drivable {
var speed: Float { get set }
}
Luego, puede agregar un comportamiento predeterminado a todos los tipos que lo conforman
extension Drivable {
func accelerate(by: Float) {
speed += by
}
}
Ahora puede crear nuevos tipos mediante la implementación Drivable
.
struct Car: Drivable {
var speed: Float = 0.0
init() {}
}
let c = Car()
c.accelerate(10)
Entonces básicamente obtienes:
Drivable
implementenspeed
Drivable
( accelerate
)Drivable
está garantizado para no ser instanciado ya que es solo un protocoloEste modelo en realidad se comporta mucho más como rasgos, lo que significa que puede ajustarse a múltiples protocolos y asumir implementaciones predeterminadas de cualquiera de ellos, mientras que con una superclase abstracta está limitado a una jerarquía de clases simple.
UICollectionViewDatasource
,. Me gustaría eliminar todo el repetitivo y encapsularlo en un protocolo / extensión separado y luego reutilizarlo en varias clases. De hecho, el patrón de plantilla sería perfecto aquí, pero ...
Creo que esto es lo más cercano a Java abstract
o C # abstract
:
class AbstractClass {
private init() {
}
}
Tenga en cuenta que, para que los private
modificadores funcionen, debe definir esta clase en un archivo Swift separado.
EDITAR: Aún así, este código no permite declarar un método abstracto y, por lo tanto, forzar su implementación.
La forma más simple es utilizar una llamada al fatalError("Not Implemented")
método abstracto (no variable) en la extensión del protocolo.
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()
pero lo hace! La clave está incluida myMethod
en la declaración del protocolo; de lo contrario, la llamada se bloquea.
Después de luchar durante varias semanas, finalmente me di cuenta de cómo traducir una clase abstracta de Java / PHP a 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());
}
}
Sin embargo, creo que Apple no implementó clases abstractas porque generalmente usa el patrón delegado + protocolo en su lugar. Por ejemplo, el mismo patrón anterior se haría mejor así:
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());
}
}
Necesitaba este tipo de patrón porque quería comúnizar algunos métodos en UITableViewController como viewWillAppear, etc. ¿Fue útil?
Hay una manera de simular clases abstractas usando Protocolos. Esto es un ejemplo:
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
}
}
Una forma más de cómo puede implementar la clase abstracta es bloquear el inicializador. Lo he hecho de esta manera:
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")
}
}
}
Estaba tratando de hacer una Weather
clase abstracta, pero el uso de protocolos no era ideal ya que tenía que escribir los mismos init
métodos una y otra vez. Extender el protocolo y escribir un init
método tenía sus problemas, especialmente porque estaba usando la NSObject
conformidad con NSCoding
.
Entonces se me ocurrió esto por la NSCoding
conformidad:
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 cuanto a init
:
fileprivate init(param: Any...) {
// Initialize
}
Mueva todas las referencias a propiedades abstractas y métodos de la clase Base a la implementación de extensión de protocolo, donde Auto restricción a la clase Base. Obtendrá acceso a todos los métodos y propiedades de la clase Base. Además, el compilador verifica la implementación de métodos abstractos y propiedades en el protocolo para clases derivadas
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 limitación de ningún despacho dinámico, puede hacer algo como esto:
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()