Swiftの静的関数とクラス関数の違いは何ですか?


334

これらの定義はSwiftライブラリで確認できます。

extension Bool : BooleanLiteralConvertible {
    static func convertFromBooleanLiteral(value: Bool) -> Bool
}

protocol BooleanLiteralConvertible {
    typealias BooleanLiteralType
    class func convertFromBooleanLiteral(value: BooleanLiteralType) -> Self
}

として定義されているメンバー関数ととして定義されstatic funcている別のメンバー関数の違いは何class funcですか?それは単にstatic、構造体と列挙型の静的関数、そしてclassクラスとプロトコルのためのものですか?他に知っておくべき違いはありますか?構文自体にこの違いがある理由は何ですか?


3
本当に違いはありません。彼らは私が推測する構造体でクラスfuncを使用できなかったため、静的funcでした。struct funcは良い候補でしょう。あなたが私に尋ねるならば、これは少しエッジの効いたですが、まあ、それらは言葉です。
fabrice truillot de chambrier 2014

2
ボーナス質問、それから:構造体はclass func?を定義するプロトコルに準拠できますか?現在の情報では、この区別はあまり役に立たないようですね。
Jean-Philippe Pellet 2014

3
はい、できます。奇妙ではないですか?
fabrice truillot de chambrier 2014

7
圧倒的な違いは、class funcs を上書きできることです
Fattie

1
検討対象:error: class methods are only allowed within classes; use 'static' to declare a static method
ガブリエルゴンカルベス2017年

回答:


238

それは単に静的が構造体と列挙型の静的関数、クラスとプロトコルのクラスのためのものですか?

それが主な違いです。他のいくつかの違いは、クラス関数が動的にディスパッチされ、サブクラスによってオーバーライドできることです。

プロトコルはclassキーワードを使用しますが、構造体をプロトコルの実装から除外せず、代わりに静的を使用します。クラスはプロトコル用に選択されたので、静的またはクラスを表すための3番目のキーワードは必要ありません。

このトピックに関するChris Lattnerから:

構文を統一することを検討しました(たとえば、キーワードとして「type」を使用する)が、それは実際には単純なことではありません。キーワード "class"と "static"は親しみやすく、非常にわかりやすく(メソッドがどのように機能するかを理解したら)、クラスに本当に静的なメソッドを追加する可能性を広げます。このモデルの主な奇妙な点は、プロトコルがキーワードを選択する必要がある(そして "クラス"を選択した)ことですが、結局のところ、それは適切なトレードオフです。

そして、これがクラス関数のオーバーライド動作の一部を示すスニペットです:

class MyClass {
    class func myFunc() {
        println("myClass")
    }
}

class MyOtherClass: MyClass {
    override class func myFunc() {
        println("myOtherClass")
    }
}

var x: MyClass = MyOtherClass()
x.dynamicType.myFunc() //myOtherClass
x = MyClass()
x.dynamicType.myFunc() //myClass

4
ああ、クラス関数が動的にディスパッチされるという非常に重要なポイント!しかし、そのような例を提供できますか?どこかにクラス名を書かなければならないでしょう?それでは、なぜそのクラスの実装を静的に選択しないのですか?
Jean-Philippe Pellet 2014

1
別の補足的な質問:見積もりはどこから得ましたか?
Jean-Philippe Pellet 2014

私の理解では、クラス関数は内部でobjc +メソッドとほとんど同じように機能します
Connor

1
ここに簡単な回答リンクを提供できますか?stackoverflow.com/questions/29636633/…–
アレンリンリ

1
@ Jean-PhilippePellet上記の例で...のstatic func myFunc()代わりにclass func myFuncを使用すると、次のエラーl 発生します静的メソッドをオーバーライドできません。どうして?それはあたかもでマークされているかのようですfinal。詳細については。以下のnextDの回答を参照してください。またx.dynamicType、これに置き換えられましたtype(of:x)
Honey

246

より明確にするために、ここで例を挙げます、

class ClassA {
  class func func1() -> String {
    return "func1"
  }

  static func func2() -> String {
    return "func2"
  }

  /* same as above
  final class func func2() -> String {
    return "func2"
  }
  */
}

static func と同じ final class func

それがあるのでfinal、私たちは以下のように、サブクラスでそれをオーバーライドすることはできません。

class ClassB : ClassA {
  override class func func1() -> String {
    return "func1 in ClassB"
  }

  // ERROR: Class method overrides a 'final` class method
  override static func func2() -> String {
    return "func2 in ClassB"
  }
}

18
あなたはチャンピオン、素晴らしい答え..私はこの違いを求めていた..ジェイク!
Abhimanyu Rathore

5
完璧です。印象的です。
Mehul 2016年

5
これは正解としてマークする必要があります。清楚な!
abhinavroy23

1
最高の説明!これは私に別の疑いをもたらしました。「クラス関数」を使用する明確な理由はありますか?つまり、「func」を使用するだけでも同じようにオーバーライドできるので、違いは何ですか?
Marcos Reboucas

1
@MarcosReboucas私があなたの質問を正しく理解していれば、どちらもオーバーライドできますがclass func、通常とは異なりfuncます。しかしfunc、インスタンス/オブジェクト用でありclass func、クラスを通じてアクセスできますClassA.classFunc()
Jake Lin

78

私は遊び場でいくつかの実験を行い、いくつかの結論を得ました。

TL; DR ここに画像の説明を入力してください

ご覧のとおりclass、の場合、class funcまたはの使用static funcは単なる習慣の問題です。

説明付きの遊び場の例:

class Dog {
    final func identity() -> String {
        return "Once a woofer, forever a woofer!"
    }

    class func talk() -> String {
        return "Woof woof!"
    }

    static func eat() -> String {
        return "Miam miam"
    }

    func sleep() -> String {
        return "Zzz"
    }
}

class Bulldog: Dog {
    // Can not override a final function
//    override final func identity() -> String {
//        return "I'm once a dog but now I'm a cat"
//    }

    // Can not override a "class func", but redeclare is ok
    func talk() -> String {
        return "I'm a bulldog, and I don't woof."
    }

    // Same as "class func"
    func eat() -> String {
        return "I'm a bulldog, and I don't eat."
    }

    // Normal function can be overridden
    override func sleep() -> String {
        return "I'm a bulldog, and I don't sleep."
    }
}

let dog = Dog()
let bullDog = Bulldog()

// FINAL FUNC
//print(Dog.identity()) // compile error
print(dog.identity()) // print "Once a woofer, forever a woofer!"
//print(Bulldog.identity()) // compile error
print(bullDog.identity()) // print "Once a woofer, forever a woofer!"

// => "final func" is just a "normal" one but prevented to be overridden nor redeclared by subclasses.


// CLASS FUNC
print(Dog.talk()) // print "Woof woof!", called directly from class
//print(dog.talk()) // compile error cause "class func" is meant to be called directly from class, not an instance.
print(Bulldog.talk()) // print "Woof woof!" cause it's called from Bulldog class, not bullDog instance.
print(bullDog.talk()) // print "I'm a bulldog, and I don't woof." cause talk() is redeclared and it's called from bullDig instance

// => "class func" is like a "static" one, must be called directly from class or subclassed, can be redeclared but NOT meant to be overridden.

// STATIC FUNC
print(Dog.eat()) // print "Miam miam"
//print(dog.eat()) // compile error cause "static func" is type method
print(Bulldog.eat()) // print "Miam miam"
print(bullDog.eat()) // print "I'm a bulldog, and I don't eat."

// NORMAL FUNC
//print(Dog.sleep()) // compile error
print(dog.sleep()) // print "Zzz"
//print(Bulldog.sleep()) // compile error
print(bullDog.sleep()) // print "I'm a bulldog, and I don't sleep."

7
あなたの例は別の答えの主な違いとして言及されたケースをカバーしていません:class関数の動的なディスパッチ対関数の静的なバインディングstatic
Jean-Philippe Pellet 2016年

1
機能を理解するための素晴らしい説明。
Yucel Bayram

33
class func上書きできませんか?
Iulian Onofrei 2016年

9
静的メソッドをオーバーライドしようとすると、エラーが発生します。ただし、クラスメソッドオーバーライドできます。受け入れられた回答を参照してください
Honey

8
class funcオーバーライド可能です。そうでなければ、私はこれに賛成票を投じたでしょう。研究と例が大好きです!
Ben Leggiero 2017

52

タイプ変数プロパティを宣言するには、static宣言修飾子で宣言をマークします。クラスはclass、サブクラスがスーパークラスの実装をオーバーライドできるように、代わりに宣言修飾子を使用して型計算プロパティをマークする場合があります。タイププロパティについては、タイププロパティで説明します。


クラス宣言では、キーワードstaticは、宣言修飾子classfinal宣言修飾子の両方で宣言をマークするのと同じ効果があります。

ソース:Swiftプログラミング言語-型変数プロパティ


5
問題は「静的関数」と「クラス関数」についてです。タイププロパティについては問いません。したがって、これは質問の答えにはなりません。ただし、プロパティに関するこれらのキーワードのコンテキストを理解することも重要です。
etayluz、2016

この答えは単に間違った質問に関するものです、おそらくここに誤って投稿されましたか?
Fattie

15

アップル社が発行したSwift 2.2 Bookによれば、

staticメソッドのfuncキーワードの前にキーワードを記述することで、タイプメソッドを示します。クラスは、classキーワード使用して、サブクラスがそのメソッドのスーパークラスの実装をオーバーライドできるようにすることもできます


10

Swift2.0以降、Appleは次のように述べています。

「プロトコルでタイププロパティを定義するときは、常に静的キーワードをタイププロパティ要件の前に付けます。クラスによって実装される場合、タイププロパティ要件の前にクラスまたは静的キーワードを付けることができますが、このルールは関係します。」


4

この例はすべての側面をクリアします!

import UIKit

class Parent {
    final func finalFunc() -> String { // Final Function, cannot be redeclared.
        return "Parent Final Function."
    }

    static func staticFunc() -> String { // Static Function, can be redeclared.
        return "Parent Static Function."
    }

    func staticFunc() -> String { // Above function redeclared as Normal function.
        return "Parent Static Function, redeclared with same name but as non-static(normal) function."
    }

    class func classFunc() -> String { // Class Function, can be redeclared.
        return "Parent Class Function."
    }

    func classFunc() -> String { // Above function redeclared as Normal function.
        return "Parent Class Function, redeclared with same name but as non-class(normal) function."
    }

    func normalFunc() -> String { // Normal function, obviously cannot be redeclared.
        return "Parent Normal Function."
    }
}

class Child:Parent {

    // Final functions cannot be overridden.

    override func staticFunc() -> String { // This override form is of the redeclared version i.e: "func staticFunc()" so just like any other function of normal type, it can be overridden.
        return "Child Static Function redeclared and overridden, can simply be called Child Normal Function."
    }

    override class func classFunc() -> String { // Class function, can be overidden.
        return "Child Class Function."
    }

    override func classFunc() -> String { // This override form is of the redeclared version i.e: "func classFunc()" so just like any other function of normal type, it can be overridden.
        return "Child Class Function, redeclared and overridden, can simply be called Child Normal Function."
    }

    override func normalFunc() -> String { // Normal function, can be overridden.
        return "Child Normal Function."
    }
}

let parent = Parent()
let child = Child()

// Final
print("1. " + parent.finalFunc())   // 1. Can be called by object.
print("2. " + child.finalFunc())    // 2. Can be called by object, parent(final) function will be called.
// Parent.finalFunc()               // Cannot be called by class name directly.
// Child.finalFunc()                // Cannot be called by class name directly.

// Static
print("3. " + parent.staticFunc())  // 3. Cannot be called by object, this is redeclared version (i.e: a normal function).
print("4. " + child.staticFunc())   // 4. Cannot be called by object, this is override form redeclared version (normal function).
print("5. " + Parent.staticFunc())  // 5. Can be called by class name directly.
print("6. " + Child.staticFunc())   // 6. Can be called by class name direcly, parent(static) function will be called.

// Class
print("7. " + parent.classFunc())   // 7. Cannot be called by object, this is redeclared version (i.e: a normal function).
print("8. " + child.classFunc())    // 8. Cannot be called by object, this is override form redeclared version (normal function).
print("9. " + Parent.classFunc())   // 9. Can be called by class name directly.
print("10. " + Child.classFunc())   // 10. Can be called by class name direcly, child(class) function will be called.

// Normal
print("11. " + parent.normalFunc())  // 11. Can be called by object.
print("12. " + child.normalFunc())   // 12. Can be called by object, child(normal) function will be called.
// Parent.normalFunc()               // Cannot be called by class name directly.
// Child.normalFunc()                // Cannot be called by class name directly.

/*
 Notes:
 ___________________________________________________________________________
 |Types------Redeclare------Override------Call by object------Call by Class|
 |Final----------0--------------0---------------1------------------0-------|
 |Static---------1--------------0---------------0------------------1-------|
 |Class----------1--------------1---------------0------------------1-------|
 |Normal---------0--------------1---------------1------------------0-------|
 ---------------------------------------------------------------------------

 Final vs Normal function: Both are same but normal methods can be overridden.
 Static vs Class function: Both are same but class methods can be overridden.
 */

出力: すべてのタイプの関数を出力する


-6

これはタイプメソッドと呼ばれ、インスタンスメソッドと同様にドット構文で呼び出されます。ただし、その型のインスタンスではなく、その型に対して型メソッドを呼び出します。以下は、SomeClassというクラスの型メソッドを呼び出す方法です。


1
class SomeClass {class func someTypeMethod(){//ここに型メソッドの実装が含まれます}} SomeClass.someTypeMethod()
Kumar Utsav

これはまったく質問に答えません。staticclassキーワードの違いを尋ねました。
Doug McBride
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.