Apa perbedaan antara fungsi statis dan fungsi kelas di Swift?

334

Saya bisa melihat definisi ini di perpustakaan Swift:

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

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

Apa perbedaan antara fungsi anggota yang didefinisikan static funcdan yang lainnya didefinisikan sebagai class func? Apakah itu hanya staticuntuk fungsi statis dari struct dan enum, dan classuntuk kelas dan protokol? Apakah ada perbedaan lain yang harus diketahui? Apa alasan untuk memiliki perbedaan dalam sintaksis itu sendiri?

Jean-Philippe Pellet
sumber
3
Tidak ada perbedaan sebenarnya. Mereka tidak bisa menggunakan func kelas dalam struct saya kira, karenanya func statis. struct func akan menjadi kandidat yang baik. Ini sedikit tegang jika Anda bertanya kepada saya tapi itu kata-katanya.
fabrice truillot de chambrier
2
Pertanyaan bonus, kemudian: dapatkah struct mematuhi protokol yang mendefinisikan class func? Dengan informasi yang kita miliki sekarang, perbedaan ini agaknya tidak berguna, bukan?
Jean-Philippe Pellet
3
ya kamu bisa. Aneh bukan?
fabrice truillot de chambrier
7
perbedaan yang luar biasa adalah Anda dapat menimpa class funcs
Fattie
1
Untuk dipertimbangkan:error: class methods are only allowed within classes; use 'static' to declare a static method
Gabriel Goncalves

Jawaban:

238

Apakah hanya itu statis untuk fungsi statis struct dan enum, dan kelas untuk kelas dan protokol?

Itulah perbedaan utama. Beberapa perbedaan lain adalah bahwa fungsi-fungsi kelas secara dinamis dikirim dan dapat ditimpa oleh subclass.

Protokol menggunakan kata kunci kelas, tetapi tidak mengecualikan struct dari penerapan protokol, mereka hanya menggunakan statis. Kelas dipilih untuk protokol sehingga tidak perlu ada kata kunci ketiga untuk mewakili statis atau kelas.

Dari Chris Lattner tentang topik ini:

Kami mempertimbangkan untuk menyatukan sintaks (mis. Menggunakan "ketik" sebagai kata kunci), tetapi itu sebenarnya bukan sekadar hal. Kata kunci "kelas" dan "statis" baik untuk keakraban dan cukup deskriptif (setelah Anda memahami cara kerja + metode), dan buka pintu untuk kemungkinan menambahkan metode yang benar-benar statis ke kelas. Keanehan utama dari model ini adalah protokol harus memilih kata kunci (dan kami memilih "kelas"), tetapi pada keseimbangan itu adalah tradeoff yang tepat.

Dan inilah cuplikan yang menunjukkan beberapa perilaku override fungsi kelas:

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
Connor
sumber
4
Aha, poin yang sangat penting bahwa fungsi kelas dikirimkan secara dinamis! Tetapi bisakah Anda memberikan contoh seperti itu? Anda harus menulis nama kelas di suatu tempat, bukan? Jadi mengapa tidak secara statis memilih implementasi kelas itu?
Jean-Philippe Pellet
1
Pertanyaan pelengkap lain: dari mana Anda mendapatkan penawarannya?
Jean-Philippe Pellet
Pemahaman saya adalah bahwa fungsi kelas bekerja hampir sama dengan metode objc + di bawah tenda
Connor
1
Bisakah saya memberikan tautan jawaban yang lebih sederhana di sini? stackoverflow.com/questions/29636633/…
allenlinli
1
@ Jean-PhilippePellet Pada contoh di atas ... jika Anda menggunakan static func myFunc()alih-alih, class func myFuncAnda akan mendapatkan kesalahan berikut l: tidak dapat mengganti metode statis . Mengapa? Karena itu seolah-olah ditandai dengan final. Untuk informasi lebih lanjut. Lihat jawaban nextD di bawah ini. Juga x.dynamicTypesekarang telah digantikan dengantype(of:x)
Madu
246

Agar lebih jelas, saya membuat contoh di sini,

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 sama dengan final class func

Karena itu final, kita tidak dapat menimpanya dalam subkelas seperti di bawah ini:

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"
  }
}
Jake Lin
sumber
18
Anda juara, jawaban yang bagus .. saya mencari perbedaan ini .. Jake !!
Abhimanyu Rathore
5
Sempurna. Impresif.
Mehul
5
Ini harus ditandai sebagai jawaban yang benar. Rapi dan bersih!
abhinavroy23
1
Penjelasan terbaik! Ini membuat saya ragu lagi. Apakah ada alasan eksplisit untuk menggunakan 'kelas func'? Maksud saya jika Anda hanya menggunakan 'func' itu juga dapat ditimpa dengan cara yang sama, jadi apa bedanya?
Marcos Reboucas
1
@MarcosReboucas jika saya memahami pertanyaan Anda dengan benar, class funcberbeda dari normal funcmeskipun keduanya dapat ditimpa. Tetapi funcuntuk instance / objek dan class funcdapat diakses melalui kelas sepertiClassA.classFunc()
Jake Lin
78

Saya melakukan beberapa percobaan di taman bermain dan mendapatkan beberapa kesimpulan.

TL; DR masukkan deskripsi gambar di sini

Seperti yang Anda lihat, dalam kasus class, penggunaan class funcatau static funchanya masalah kebiasaan.

Contoh taman bermain dengan penjelasan:

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."
Nhon Nguyen
sumber
7
Contoh Anda tidak mencakup kasus yang disebutkan sebagai perbedaan utama dalam jawaban lain: pengiriman classfungsi dinamis vs yang mengikat statis static.
Jean-Philippe Pellet
1
Penjelasan bagus untuk memahami fungsi.
Yucel Bayram
34
Tidak bisa class funcditimpa?
Iulian Onofrei
9
Jika Anda mencoba mengganti metode statis, Anda AKAN MENDAPAT KESALAHAN. Namun Anda dapat mengganti metode kelas. Lihat jawaban yang diterima
Sayang
8
class funcbisa ditimpa. Saya akan memilih ini sebaliknya; suka penelitian dan contoh!
Ben Leggiero
52

Untuk mendeklarasikan properti tipe variabel, tandai deklarasi dengan staticpengubah deklarasi. Kelas dapat menandai tipe properti yang dikomputasi dengan classpengubah deklarasi alih-alih untuk memungkinkan subclass untuk mengesampingkan implementasi superclass. Tipe properti dibahas dalam Tipe Properties.

CATATAN
Dalam deklarasi kelas, kata kunci staticmemiliki efek yang sama dengan menandai deklarasi dengan pengubah deklarasi classdan final.

Sumber: Bahasa Pemrograman Swift - Tipe Variable Properties

NexD.
sumber
5
Pertanyaannya adalah tentang 'fungsi statis' dan 'fungsi kelas'. BUKAN menanyakan tentang Tipe Properties. Jadi ini tidak menjawab pertanyaan - meskipun penting untuk memahami konteks kata kunci ini sehubungan dengan properti juga.
etayluz
Jawaban ini hanya pada pertanyaan yang salah, mungkin itu diposting di sini secara tidak sengaja?
Fattie
15

Menurut Swift 2.2 Book yang diterbitkan oleh apel:

“Anda menunjukkan metode jenis dengan menulis statickata kunci sebelum kata kunci func metode. Kelas juga dapat menggunakan classkata kunci untuk memungkinkan subclass untuk mengesampingkan penerapan metode tersebut pada superclass . "

Milad
sumber
10

Dari Swift2.0, Apple mengatakan:

"Selalu awali persyaratan jenis properti dengan kata kunci statis saat Anda mendefinisikannya dalam protokol. Aturan ini berkaitan meskipun persyaratan jenis properti dapat diawali dengan kelas atau kata kunci statis saat diterapkan oleh suatu kelas:"

Jacky
sumber
5

Contoh ini akan menghapus setiap aspek!

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.
 */

Keluaran: Keluarkan semua jenis fungsi

Rehan Ali Khan
sumber
-6

Ini disebut metode tipe, dan disebut dengan sintaks dot, seperti metode instance. Namun, Anda memanggil metode tipe pada tipe, bukan pada instance dari tipe itu. Inilah cara Anda memanggil metode tipe pada kelas yang disebut SomeClass:

Kumar Utsav
sumber
1
class SomeClass {class func someTypeMethod () {// implementasi metode ketik di sini}} SomeClass.someTypeMethod ()
Kumar Utsav
Ini sama sekali tidak menjawab pertanyaan. Dia bertanya perbedaan antara staticdan classkata kunci.
Doug McBride