web-dev-qa-db-ja.com

Swift言語の抽象関数

Swift言語で抽象関数を作成したいと思います。出来ますか?

class BaseClass {
    func abstractFunction() {
        // How do I force this function to be overridden?
    }
}

class SubClass : BaseClass {
    override func abstractFunction() {
        // Override
    }
}
115
kev

Swift(Objective-Cなど)には抽象概念はありませんが、これを行うことができます。

class BaseClass {
    func abstractFunction() {
        preconditionFailure("This method must be overridden") 
    } 
}

class SubClass : BaseClass {
     override func abstractFunction() {
         // Override
     } 
}
180
jaumard

必要なのは基本クラスではなく、プロトコルです。

protocol MyProtocol {
    func abstractFunction()
}

class MyClass : MyProtocol {
    func abstractFunction() {
    }
}

クラスでabstractFunctionを指定しない場合、エラーになります。

まだ他の動作のためにベースクラスが必要な場合、これを行うことができます:

class MyClass : BaseClass, MyProtocol {
    func abstractFunction() {
    }
}
33
Steve Waddicor

抽象基本クラスをサポートするプラットフォームからかなりの量のコードをSwiftに移植し、これを頻繁に実行します。本当に必要なものが抽象基本クラスの機能である場合、それはこのクラスが共有ベースのクラス機能の実装として機能することを意味します(それ以外の場合は単にインターフェイス/プロトコルになります)。派生クラス。

Swiftでこれを行うには、プロトコルと基本クラスが必要になります。

protocol Thing
{
    func sharedFunction()
    func abstractFunction()
}

class BaseThing
{
    func sharedFunction()
    {
        println("All classes share this implementation")
    }
}

基本クラスは共有メソッドを実装しますが、プロトコルは実装しません(すべてのメソッドを実装するわけではないため)。

次に、派生クラスで:

class DerivedThing : BaseThing, Thing 
{
    func abstractFunction() 
    {
        println("Derived classes implement this");
    }
}

派生クラスは基本クラスからsharedFunctionを継承し、プロトコルのその部分を満たすのに役立ちます。プロトコルは、抽象クラスを実装するために派生クラスを引き続き必要とします。

このメソッドの唯一の本当の欠点は、基本クラスがプロトコルを実装しないため、プロトコルプロパティ/メソッドにアクセスする必要がある基本クラスメソッドがある場合、派生クラスでそれをオーバーライドする必要があることです。基本クラスが(スーパー経由で)selfを渡して、基本クラスがその作業を行うプロトコルのインスタンスを持つようにします。

たとえば、sharedFunctionをabstractFunctionを呼び出す必要があるとしましょう。プロトコルは同じままで、クラスは次のようになります。

class BaseThing
{
    func sharedFunction(thing: Thing)
    {
        println("All classes share this implementation")
        thing.abstractFunction()
    }
}

class DerivedThing : BaseThing, Thing 
{
    func sharedFunction()
    {
        super.sharedFunction(self)
    }

    func abstractFunction() 
    {
        println("Derived classes implement this");
    }
}

これで、派生クラスのsharedFunctionはプロトコルのその部分を満たしますが、派生クラスはそれでもかなり簡単な方法で基本クラスのロジックを共有できます。

28
BobDickinson

これは、AppleがUIKitで抽象メソッドを処理する「公式」な方法のようです。 UITableViewControllerおよびUITableViewDelegateでの動作方法をご覧ください。最初に行うことの1つは、行delegate = selfを追加することです。まあ、それはまさにトリックです。

protocol AbstractMethodsForClassX {
    func abstractMethod() -> String
}
/// It takes an implementation of the protocol as property (same like the delegate in UITableViewController does)
/// And does not implement the protocol as it does not implement the abstract methods. It has the abstract methods available in the `delegate`
class BaseClassX {
    var delegate: AbstractMethodsForClassX!

    func doSomethingWithAbstractMethod() -> String {
        return delegate.abstractMethod() + " - And I believe it"
    }
}
/// First and only additional thing you have to do, you must set the "delegate" property
class ClassX: BaseClassX, AbstractMethodsForClassX {
    override init() {
        super.init()
        delegate = self
    }

    func abstractMethod() -> String {return "Yes, this works!"}
}
let x = ClassX()
x.doSomethingWithAbstractMethod()

出力を確認するには、プレイグラウンドで確認してください。

いくつかの発言

  • 最初に、多くの答えがすでに与えられました。誰かがこれまでずっと見つけてくれることを願っています。
  • 問題は、実際に以下を実装するパターンを見つけることでした:
    • クラスはメソッドを呼び出します。メソッドは派生サブクラスの1つで実装する必要があります(オーバーライドされます)
    • 最良の場合、メソッドがサブクラスでオーバーライドされない場合、コンパイル時間の間にエラーが発生します
  • 抽象メソッドに関することは、それらがインターフェイス定義と基本クラスの実際の実装の一部の混合物であることです。両方同時に。 Swiftは非常に新しく、非常にきれいに定義されているため、そのような便利さはありませんが、「汚れた」概念が(まだ)あります。
  • 私にとって(貧しい古いJava男)、この問題は時々進化します。私はこの投稿のすべての回答を読んでおり、今回は実行可能なパターンを見つけたと思う-少なくとも私には。
  • 更新:AppleのUIKit実装者は同じパターンを使用しているようです。 UITableViewControllerUITableViewDelegateを実装しますが、delegateプロパティを明示的に設定してデリゲートとして登録する必要があります。
  • これはすべてXcode 7.3.1のPlaygroundでテストされています
21
jboi

これを行う1つの方法は、基本クラスで定義されたオプションのクロージャーを使用することです。子はそれを実装するかどうかを選択できます。

class BaseClass {
    var abstractClosure?:(()->())?
    func someFunc()
    {
        if let abstractClosure=abstractClosure
        {
            abstractClosure()
        }
    } 
}

class SubClass : BaseClass {
    init()
    {
        super.init()
        abstractClosure={ ..... }
    }
}
7
hariseldon78

私はあなたが今していることを理解しています。

protocol BaseProtocol {
    func abstractFunction()
}

次に、プロトコルに準拠します。

class SubClass : BaseProtocol {

    func abstractFunction() {
        // Override
        println("Override")
    }
}

クラスがサブクラスでもある場合、プロトコルはスーパークラスに従います。

class SubClass: SuperClass, ProtocolOne, ProtocolTwo {}
5
Logan

まあ、私はゲームに遅れていること、そして起こった変化を利用しているかもしれないことを知っています。ごめんなさい.

いずれにしても、私は答えを提供したいと思います。私はテストをするのが好きで、fatalError()のソリューションはAFAIKであり、テストできません。

もっとswiftyアプローチを使用することをお勧めします。あなたの目標は、いくつかの共通の詳細を含む抽象化を定義することですが、それは完全には定義されていません。つまり、抽象メソッドです。定義されているメソッドと定義されていないメソッドの両方で、抽象化で予想されるすべてのメソッドを定義するプロトコルを使用します。次に、ケースで定義されているメソッドを実装するプロトコル拡張を作成します。最後に、派生クラスはすべてのメソッドを意味するプロトコルを実装する必要がありますが、プロトコル拡張の一部であるメソッドにはすでに実装があります。

1つの具体的な関数を使用して例を拡張します。

protocol BaseAbstraction {
    func abstractFunction() {
        // How do I force this function to be overridden?
    }
}

extension BaseAbstraction {
    func definedFunction() {
        print("Hello")
}

class SubClass : BaseAbstraction {
    func abstractFunction() {
        // No need to "Override". Just implement.
    }
}

これを行うことにより、コンパイラが再びあなたの友人であることに注意してください。メソッドが「オーバーライド」されていない場合は、fatalError()または実行時に発生する例外で発生するエラーではなく、コンパイル時にエラーが発生します。

4
Jorge Ortiz

assertキーワードを使用して抽象メソッドを強制する:

class Abstract
{
    func doWork()
    {
        assert(false, "This method must be overriden by the subclass")
    }
}

class Concrete : Abstract
{
    override func doWork()
    {
        println("Did some work!")
    }
}

let abstract = Abstract()
let concrete = Concrete()

abstract.doWork()    // fails
concrete.doWork()    // OK

ただし、Steve Waddicorで述べたように、おそらくprotocolが代わりに必要です。

3
Erik

私は質問を理解し、同じ解決策を探していました。プロトコルは、抽象メソッドとは異なります。

プロトコルでは、クラスがそのようなプロトコルに準拠することを指定する必要があります。抽象メソッドは、そのようなメソッドをオーバーライドする必要があることを意味します。

つまり、プロトコルは一種のオプションであり、基本クラスとプロトコルを指定する必要があります。プロトコルを指定しない場合、そのようなメソッドをオーバーライドする必要はありません。

抽象メソッドは、基本クラスが必要であるが、独自のメソッドを1つまたは2つ実装する必要があることを意味します。これは同じではありません。

同じ振る舞いが必要なので、解決策を探していました。 Swiftにはそのような機能がないと思います。

2
Marvin Aguero

この問題には別の選択肢がありますが、@ jaumardの提案と比べると依然としてマイナス面があります。 returnステートメントが必要です。例外を直接スローすることで構成されているため、それを必要とするポイントを逃していますが:

class AbstractMethodException : NSException {

    init() {
        super.init(
            name: "Called an abstract method",
            reason: "All abstract methods must be overriden by subclasses",
            userInfo: nil
        );
    }
}

その後:

class BaseClass {
    func abstractFunction() {
        AbstractMethodException.raise();
    }
}

その後に来るものはすべて到達不能であるため、なぜ強制的に返品するのかわかりません。

1
André Fratelli

役に立つかどうかはわかりませんが、SpritKitゲームをビルドしようとしているときに抽象メソッドで同様の問題が発生しました。私が欲しかったのは、move()、run()などのメソッドを持つ抽象Animalクラスですが、スプライト名(およびその他の機能)はクラスの子によって提供されるべきです。だから私はこのようなことをしてしまった(Swift 2のテスト):

import SpriteKit

// --- Functions that must be implemented by child of Animal
public protocol InheritedAnimal
{
    func walkSpriteNames() -> [String]
    func runSpriteNames() -> [String]
}


// --- Abstract animal
public class Animal: SKNode
{
    private let inheritedAnimal: InheritedAnimal

    public init(inheritedAnimal: InheritedAnimal)
    {
        self.inheritedAnimal = inheritedAnimal
        super.init()
    }

    public required init?(coder aDecoder: NSCoder)
    {
        fatalError("NSCoding not supported")
    }

    public func walk()
    {
        let sprites = inheritedAnimal.walkSpriteNames()
        // create animation with walking sprites...
    }

    public func run()
    {
        let sprites = inheritedAnimal.runSpriteNames()
        // create animation with running sprites
    }
}


// --- Sheep
public class SheepAnimal: Animal
{
    public required init?(coder aDecoder: NSCoder)
    {
        fatalError("NSCoding not supported")
    }

    public required init()
    {
        super.init(inheritedAnimal: InheritedAnimalImpl())
    }

    private class InheritedAnimalImpl: InheritedAnimal
    {
        init() {}

        func walkSpriteNames() -> [String]
        {
            return ["sheep_step_01", "sheep_step_02", "sheep_step_03", "sheep_step_04"]
        }

        func runSpriteNames() -> [String]
        {
            return ["sheep_run_01", "sheep_run_02"]
        }
    }
}
1
interrupt