Existe uma maneira de criar uma classe abstrata na linguagem Swift, ou isso é uma limitação como Objective-C? Eu gostaria de criar uma classe abstrata comparável ao que Java define como uma classe abstrata.
Existe uma maneira de criar uma classe abstrata na linguagem Swift, ou isso é uma limitação como Objective-C? Eu gostaria de criar uma classe abstrata comparável ao que Java define como uma classe abstrata.
Respostas:
Não há classes abstratas no Swift (assim como Objective-C). Sua melhor aposta será usar um protocolo , que é como uma interface Java.
Com o Swift 2.0, você pode adicionar implementações de métodos e implementações de propriedades calculadas usando extensões de protocolo. Suas únicas restrições são que você não pode fornecer variáveis ou constantes de membros e não há despacho dinâmico .
Um exemplo dessa técnica seria:
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
Observe que isso está fornecendo recursos de "classe abstrata", mesmo para estruturas, mas as classes também podem implementar o mesmo protocolo.
Observe também que toda classe ou estrutura que implementa o protocolo Employee precisará declarar a propriedade annualSalary novamente.
Mais importante, observe que não há despacho dinâmico . Quando logSalary
é chamado na instância que é armazenada como SoftwareEngineer
chama a versão substituída do método. Quando logSalary
é chamado na instância após ser convertido em um Employee
, ele chama a implementação original (não envia dinamicamente para a versão substituída, mesmo que a instância seja realmente a Software Engineer
.
Para obter mais informações, consulte o excelente vídeo da WWDC sobre esse recurso: Criando aplicativos melhores com tipos de valor no Swift
protocol Animal { var property : Int { get set } }
. Você também pode deixar o set se você não quer que a propriedade de ter um setter
func logSalary()
à declaração de protocolo do funcionário, o exemplo será impresso overridden
para as duas chamadas para logSalary()
. Isso está no Swift 3.1. Assim, você obtém os benefícios do polimorfismo. O método correto é chamado nos dois casos.
Observe que esta resposta está direcionada ao Swift 2.0 e superior
Você pode obter o mesmo comportamento com protocolos e extensões de protocolo.
Primeiro, você escreve um protocolo que atua como uma interface para todos os métodos que precisam ser implementados em todos os tipos que estejam em conformidade.
protocol Drivable {
var speed: Float { get set }
}
Em seguida, você pode adicionar um comportamento padrão a todos os tipos que estão em conformidade com ele
extension Drivable {
func accelerate(by: Float) {
speed += by
}
}
Agora você pode criar novos tipos implementando Drivable
.
struct Car: Drivable {
var speed: Float = 0.0
init() {}
}
let c = Car()
c.accelerate(10)
Então, basicamente, você obtém:
Drivable
implementemspeed
Drivable
( accelerate
)Drivable
é garantido para não ser instanciado, pois é apenas um protocoloNa verdade, esse modelo se comporta muito mais como características, o que significa que você pode estar em conformidade com vários protocolos e assumir implementações padrão de qualquer um deles, enquanto que com uma superclasse abstrata, você está limitado a uma hierarquia de classes simples.
UICollectionViewDatasource
,. Gostaria de remover todo o clichê e encapsular em protocolo / extensão separados e depois reutilizá-lo por várias classes. Na verdade, o padrão modelo seria perfeito aqui, mas ...
Eu acho que este é o mais próximo de Java abstract
ou C # abstract
:
class AbstractClass {
private init() {
}
}
Observe que, para que os private
modificadores funcionem, você deve definir esta classe em um arquivo Swift separado.
EDIT: Ainda assim, este código não permite declarar um método abstrato e, portanto, força sua implementação.
A maneira mais simples é usar uma chamada para fatalError("Not Implemented")
o método abstrato (não variável) na extensão do 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()
mas funciona! A chave está incluída myMethod
na declaração do protocolo; caso contrário, a chamada trava.
Depois de lutar por várias semanas, finalmente percebi como traduzir uma classe abstrata Java / PHP para 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());
}
}
No entanto, acho que a Apple não implementou classes abstratas porque geralmente usa o padrão delegate + protocol. Por exemplo, o mesmo padrão acima seria melhor assim:
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());
}
}
Eu precisava desse tipo de padrão porque queria comumizar alguns métodos no UITableViewController, como viewWillAppear etc. Isso foi útil?
Existe uma maneira de simular classes abstratas usando Protocolos. Isto é um exemplo:
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
}
}
Mais uma maneira de implementar a classe abstrata é bloquear o inicializador. Eu fiz dessa maneira:
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")
}
}
}
Eu estava tentando criar uma Weather
classe abstrata, mas o uso de protocolos não era o ideal, pois tive que escrever os mesmos init
métodos repetidamente. Estender o protocolo e escrever um init
método teve seus problemas, principalmente porque eu estava usando a NSObject
conformidade NSCoding
.
Então, eu vim com isso para a NSCoding
conformidade:
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...
}
Quanto a init
:
fileprivate init(param: Any...) {
// Initialize
}
Mova todas as referências para propriedades abstratas e métodos da classe Base para implementação de extensão de protocolo, em que Auto-restrição para a classe Base. Você terá acesso a todos os métodos e propriedades da classe Base. Adicionalmente, o compilador verifica a implementação de métodos e propriedades abstratas no protocolo para classes 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;
}
}
Com a limitação de nenhum despacho dinâmico, você pode fazer algo assim:
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()