回答:
Swiftには抽象クラスはありません(Objective-Cと同様)。あなたの最善の策は、Javaインターフェースのようなプロトコルを使用することです。
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での値タイプによるより良いアプリの構築
protocol Animal { var property : Int { get set } }
。プロパティにセッターを設定したくない場合は、セットを
func logSalary()
Employeeプロトコル宣言に追加するだけの場合、例ではoverridden
への両方の呼び出しが出力されますlogSalary()
。これはSwift 3.1にあります。したがって、ポリモーフィズムの利点が得られます。どちらの場合も、正しいメソッドが呼び出されます。
この回答は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
のが実装speed
Drivable
(accelerate
)にDrivable
これは単なるプロトコルであるため、インスタンス化されないことが保証されていますこのモデルは実際にはトレイトのように動作します。つまり、複数のプロトコルに準拠し、それらのいずれかのデフォルト実装を実行できますが、抽象スーパークラスでは単純なクラス階層に制限されます。
UICollectionViewDatasource
。すべてのボイラープレートを削除し、個別のプロトコル/拡張機能にカプセル化してから、複数のクラスで再利用したいと思います。実際、ここではテンプレートパターンは完璧ですが、
これはJava abstract
またはC#に最も近いと思いますabstract
。
class AbstractClass {
private init() {
}
}
なお、 private
修飾子が機能このクラスを別のSwiftファイルで定義する必要が。
編集:それでも、このコードは抽象メソッドを宣言することを許可しないため、その実装を強制します。
最も簡単な方法は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()
(MyConcreteClass() as MyInterface).myMethod()
!キーはmyMethod
プロトコル宣言に含まれています。そうしないと、呼び出しがクラッシュします。
数週間苦労した後、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のいくつかのメソッドを共通化したかったため、この種のパターンが必要でした。これは役に立ちましたか?
プロトコルを使用して抽象クラスをシミュレートする方法があります。これは例です:
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
}
}
抽象クラスを実装するもう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")
}
}
}
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
}
Baseクラスの抽象プロパティおよびメソッドへのすべての参照を、自己拡張がBaseクラスにあるプロトコル拡張実装に移動します。Baseクラスのすべてのメソッドとプロパティにアクセスできます。さらに、コンパイラは、派生クラスのプロトコルでの抽象メソッドとプロパティの実装をチェックします
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()