Swift言語で抽象クラスを作成する方法はありますか、またはこれはObjective-Cのような制限ですか? Javaが抽象クラスとして定義するものに匹敵する抽象クラスを作成したいと思います。
Swiftには抽象クラスはありません(Objective-Cと同様)。あなたの最善の策は、Javaインターフェイスのような Protocol を使用することです。
Swift 2.0を使用すると、プロトコル拡張を使用してメソッドの実装と計算されたプロパティの実装を追加できます。唯一の制限は、メンバー変数または定数を提供できないこと、および動的ディスパッチがないことです。
この手法の例は次のとおりです。
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
これは、構造体に対しても機能のような「抽象クラス」を提供しますが、クラスは同じプロトコルを実装することもできます。
また、Employeeプロトコルを実装するすべてのクラスまたは構造体は、annualSalaryプロパティを再度宣言する必要があることに注意してください。
最も重要なことは、動的ディスパッチがないことです。 logSalary
として保存されているインスタンスでSoftwareEngineer
が呼び出されると、オーバーライドされたバージョンのメソッドが呼び出されます。 logSalary
にキャストされた後にインスタンスでEmployee
が呼び出されると、元の実装が呼び出されます(インスタンスが実際にSoftware Engineer
であっても、オーバーライドされたバージョンに動的にディスパッチされません)。
詳細については、その機能に関する優れたWWDCビデオを確認してください。 Swiftで値型を使用したより良いアプリの構築
この回答はSwift 2.0以降を対象としています
プロトコルとプロトコル拡張を使用して同じ動作を実現できます。
まず、それに準拠するすべてのタイプで実装する必要があるすべてのメソッドのインターフェースとして機能するプロトコルを作成します。
protocol Drivable {
var speed: Float { get set }
}
次に、それに準拠するすべてのタイプにデフォルトの動作を追加できます
extension Drivable {
func accelerate(by: Float) {
speed += by
}
}
Drivable
を実装して、新しい型を作成できるようになりました。
struct Car: Drivable {
var speed: Float = 0.0
init() {}
}
let c = Car()
c.accelerate(10)
だから基本的にあなたが得る:
Drivable
sがspeed
を実装することを保証するコンパイル時チェックDrivable
(accelerate
)に準拠するすべてのタイプにdefault-behaviourを実装できますDrivable
は単なるプロトコルであるため、インスタンス化されないことが保証されていますこのモデルは実際には特性のように動作します。つまり、複数のプロトコルに準拠し、それらのいずれかのデフォルト実装を使用できますが、抽象スーパークラスでは単純なクラス階層に制限されます。
これはJavaのabstract
またはC#のabstract
に最も近いと思います。
class AbstractClass {
private init() {
}
}
private
修飾子が機能するためには、このクラスを別のSwiftファイルで定義する必要があることに注意してください。
EDIT:それでも、このコードでは抽象メソッドを宣言できないため、その実装を強制します。
数週間苦労した後、私はついにJava/PHP抽象クラスを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());
}
}
ただし、Appleは抽象クラスを実装しなかったと思います。これは、通常、代わりにデリゲート+プロトコルパターンを使用するためです。たとえば、上記の同じパターンは、次のように実行する方が適切です。
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());
}
}
ViewWillAppearなどのUITableViewControllerの一部のメソッドを共通化するため、この種のパターンが必要でした。これは役に立ちましたか?
最も簡単な方法は、プロトコル拡張の抽象メソッド(変数ではない)にfatalError("Not Implemented")
の呼び出しを使用することです。
protocol MyInterface {
func myMethod() -> String
}
extension MyInterface {
func myMethod() -> String {
fatalError("Not Implemented")
}
}
class MyConcreteClass: MyInterface {
func myMethod() -> String {
return "The output"
}
}
MyConcreteClass().myMethod()
プロトコルを使用して抽象クラスをシミュレートする方法があります。これは一例です:
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
}
}
抽象クラスを実装するもう1つの方法は、初期化子をブロックすることです。私はこの方法でそれをやった:
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")
}
}
}
基本クラスの抽象プロパティおよびメソッドへのすべての参照をプロトコル拡張実装に移動します。ここで、自己制約は基本クラスになります。基本クラスのすべてのメソッドとプロパティにアクセスできます。さらに、派生クラスのプロトコルで抽象メソッドとプロパティのコンパイラチェック実装
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;
}
}
動的ディスパッチがないという制限により、次のようなことができます。
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()
Weather
抽象クラスを作成しようとしていましたが、同じinit
メソッドを何度も記述しなければならなかったため、プロトコルの使用は理想的ではありませんでした。プロトコルを拡張してinit
メソッドを記述すると、特にNSObject
に準拠するNSCoding
を使用していたため、問題がありました。
そこで、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...
}
init
について:
fileprivate init(param: Any...) {
// Initialize
}