迅速な言語で抽象的な関数を作成したいのですが。出来ますか?
class BaseClass {
func abstractFunction() {
// How do I force this function to be overridden?
}
}
class SubClass : BaseClass {
override func abstractFunction() {
// Override
}
}
迅速な言語で抽象的な関数を作成したいのですが。出来ますか?
class BaseClass {
func abstractFunction() {
// How do I force this function to be overridden?
}
}
class SubClass : BaseClass {
override func abstractFunction() {
// Override
}
}
回答:
Swiftには(Objective-Cのような)抽象概念はありませんが、これを行うことができます:
class BaseClass {
func abstractFunction() {
preconditionFailure("This method must be overridden")
}
}
class SubClass : BaseClass {
override func abstractFunction() {
// Override
}
}
assert(false, "This method must be overriden by the subclass")
。
fatalError("This method must be overridden")
preconditionFailure("Bla bla bla")
、リリースビルドで実行され、returnステートメントの必要性を排除するSwiftもあります。編集:この方法は基本的に等しいfatalError()
が、より適切な方法であることがわかりました(より優れたドキュメント、Swiftとともにprecondition()
、assert()
およびassertionFailure()
、ここで読む)
必要なのは基本クラスではなく、プロトコルです。
protocol MyProtocol {
func abstractFunction()
}
class MyClass : MyProtocol {
func abstractFunction() {
}
}
クラスにabstractFunctionを指定しないと、エラーになります。
それでも他の動作のベースクラスが必要な場合は、これを行うことができます:
class MyClass : BaseClass, MyProtocol {
func abstractFunction() {
}
}
私は、抽象基本クラスをサポートするプラットフォームからかなりの量のコードを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を継承し、プロトコルのその部分を満たすのに役立ちます。プロトコルは、abstractFunctionを実装するために派生クラスを必要とします。
このメソッドの唯一の欠点は、基本クラスがプロトコルを実装しないため、プロトコルプロパティ/メソッドへのアクセスが必要な基本クラスメソッドがある場合、派生クラスでそれをオーバーライドし、そこから呼び出しを行う必要があることです。基本クラスが(superを介して)渡される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がプロトコルのその部分を満たしていますが、派生クラスは、基本クラスのロジックをかなり簡単な方法で共有できます。
これは、AppleがUIKitで抽象メソッドを処理する「公式の」方法のようです。UITableViewController
とそれがどのように機能するかを見てくださいUITableViewDelegate
。最初に行うことの1つは、行を追加することです。delegate = self
。まあ、それはまさにトリックです。
protocol AbstractMethodsForClassX {
func abstractMethod() -> String
}
2.基本クラスを記述します
/// 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"
}
}
3.サブクラスを記述します。
/// 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()
Playgroundで出力を確認してください。
UITableViewController
実装しますUITableViewDelegate
が、明示的にdelegate
プロパティを設定してデリゲートとして登録する必要があります。getComments
親デリゲートにメソッドがあります。コメントの種類はいくつかありますが、各オブジェクトに関連するものが必要です。そのため、delegate.getComments()
そのメソッドをオーバーライドしない場合は、サブクラスをコンパイルしないようにします。VCはParentDelegate
と呼ばれるオブジェクトdelegate
、またはBaseClassX
あなたの例ではそれがあることを知っていますBaseClassX
が、抽象化されたメソッドはありません。VCがを使用していることを具体的に知る必要がありSubclassedDelegate
ます。
これを行う1つの方法は、基本クラスで定義されたオプションのクロージャーを使用することであり、子はそれを実装するかどうかを選択できます。
class BaseClass {
var abstractClosure?:(()->())?
func someFunc()
{
if let abstractClosure=abstractClosure
{
abstractClosure()
}
}
}
class SubClass : BaseClass {
init()
{
super.init()
abstractClosure={ ..... }
}
}
ええと、私はゲームに遅れており、起こった変化を利用しているかもしれないことを知っています。申し訳ありません。
いずれにせよ、私は私の答えを提供したいと思います。なぜなら、私はテストを行うのが大好きでfatalError()
あり、AFAIK を使用したソリューションはテスト不可能であり、例外を伴うものはテストがはるかに難しいからです。
もっと使用することをお勧めします 迅速なアプローチ。あなたの目標は、いくつかの共通の詳細を持つ抽象化を定義することですが、それは完全には定義されていません。つまり、抽象メソッドです。抽象化で予想されるすべてのメソッドを定義するプロトコルを使用します。定義されたものと定義されていないものの両方を使用します。次に、ケースで定義されているメソッドを実装するプロトコル拡張を作成します。最後に、すべての派生クラスはプロトコルを実装する必要があります。つまり、すべてのメソッドを意味しますが、プロトコル拡張の一部であるものはすでに実装されています。
例を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()
や実行時に発生する例外ではなく、コンパイル時にエラーが発生します。
あなたが今何をしているのか理解しました、あなたはプロトコルを使うほうがいいと思います
protocol BaseProtocol {
func abstractFunction()
}
次に、プロトコルに準拠します。
class SubClass : BaseProtocol {
func abstractFunction() {
// Override
println("Override")
}
}
クラスがサブクラスでもある場合、プロトコルはスーパークラスに従います。
class SubClass: SuperClass, ProtocolOne, ProtocolTwo {}
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
代わりにaが必要です。
私は質問を理解し、同じ解決策を探していました。プロトコルは抽象メソッドと同じではありません。
プロトコルでは、クラスがそのようなプロトコルに準拠していることを指定する必要があります。抽象メソッドは、そのようなメソッドをオーバーライドする必要があることを意味します。
言い換えると、プロトコルはオプションの一種であり、基本クラスとプロトコルを指定する必要があります。プロトコルを指定しない場合、そのようなメソッドをオーバーライドする必要はありません。
抽象メソッドは、基本クラスが必要だが、独自のメソッドを1つまたは2つ実装する必要があることを意味します。これは同じではありません。
同じ行動が必要なので、解決策を探していました。Swiftにはそのような機能が欠けていると思います。
この問題には別の選択肢がありますが、@ 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();
}
}
それ以降は何も到達できないので、なぜ強制的に復帰させるのかわかりません。
AbstractMethodException().raise()
?
役に立つかどうかはわかりませんが、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"]
}
}
}