Яка різниця між статичним функцією та класовим функом у 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 у структурі, я думаю, отже, статичний функціонал. stru func був би хорошим кандидатом. Це трохи різко, якщо ви запитаєте мене, але добре, це слова.
фабрика truillot de chambrier

2
Тоді питання про бонус: чи може структура відповідати протоколу, який визначає class func? З інформацією, яку ми маємо зараз, ця відмінність здається досить марною, чи не так?
Жан-Філіп Пеллет

3
так, ти можеш. Дивно чи не так?
фабрика truillot de chambrier

7
переважна різниця в тому, що ви можете перемогти class funcs
Fattie

1
error: class methods are only allowed within classes; use 'static' to declare a static method
Враховувати

Відповіді:


238

Чи просто статично для статичних функцій структур і перерахунків, а клас - для класів і протоколів?

У цьому головна відмінність. Деякі інші відмінності полягають у тому, що функції класу динамічно розсилаються і можуть бути замінені підкласами.

У протоколах використовується ключове слово класу, але це не виключає структури реалізації протоколу, вони просто використовують статичні. Клас був обраний для протоколів, тому не повинно бути третього ключового слова для представлення статичного або класу.

Від Кріса Леттнера на цю тему:

Ми розглядали можливість об'єднання синтаксису (наприклад, використання "типу" як ключового слова), але це насправді не просто речі. Ключові слова "клас" і "статичний" добре підходять для ознайомлення і є досить описовими (як тільки ви зрозумієте, як працюють + методи), і відкривають двері для потенційного додавання до класів справді статичних методів. Основна дивність цієї моделі полягає в тому, що протоколи повинні вибирати ключове слово (і ми вибрали "клас"), але, на балансі, це правильний компроміс.

Ось фрагмент, який показує деяку переважну поведінку класових функцій:

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
Ага, дуже важливий момент, що функції класу динамічно розсилаються! Але чи могли б ви навести такий приклад? Вам би довелося десь написати назву класу, правда? То чому б статично не вибрати реалізацію цього класу?
Жан-Філіп Пеллет

1
Ще одне додаткове запитання: звідки ви взяли цитату?
Жан-Філіп Пеллет

я розумію, що функції класу працюють майже так само, як методи objc + під кришкою
Коннор

1
Чи можу я надати простіше посилання на відповідь тут? stackoverflow.com/questions/29636633 / ...
allenlinli

1
@ Jean-PhilippePellet У наведеному вище прикладі ... якщо ви використовуєте static func myFunc()замість class func myFuncвас, ви отримаєте таку помилку l: не можу перекрити статичний метод . Чому? Бо так, ніби його позначено final. Для отримання додаткової інформації. Дивіться відповідь nextD нижче. Також x.dynamicTypeзараз замінено наtype(of:x)
Мед

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
Ви чемпіоне, чудова відповідь .. Я шукав цієї різниці .. Джейк !!
Абхіманью Ратхор

5
Ідеально. Вражає.
Мегул

5
Це слід позначити як правильну відповідь. Охайно і чисто!
abhinavroy23

1
Найкраще пояснення! Це спричинило мене до іншого сумніву. Чи є якась явна причина використання "класового функціоналу"? Я маю на увазі, якщо ви просто використовуєте "func", це також може бути відмінено аналогічним чином, і яка різниця?
Маркос Ребукас

1
@MarcosReboucas, якщо я правильно розумію ваше запитання, class funcвідрізняється від нормального, funcхоча обидва можуть бути відмінені. Але funcце для екземпляра / об'єкта, і до нього class funcможна дійти через клас на кшталтClassA.classFunc()
Джейк Лін

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їх.
Жан-Філіп Пеллет

1
Прекрасне пояснення розуміння функцій.
Yucel Bayram

33
Не можна class funcперезаписати?
Юліан Онофрей

9
Якщо ви спробуєте замінити статичний метод, ви отримаєте помилку. Однак ви можете перекрити метод класу. Дивіться прийняту відповідь
Мед

8
class funcє переборним. Я хотів би проголосувати це інакше; любить дослідження та приклад!
Ben Leggiero

52

Щоб оголосити властивість змінної типу, позначте staticдекларацію модифікатором декларації. classЗамість цього класи можуть позначати обчислені властивості за допомогою модифікатора декларації, щоб дозволяти підкласам переосмислювати реалізацію надкласу. Властивості типу обговорюються у Властивості типу.

ПРИМІТКА
У декларації класу ключове слово staticмає такий же ефект, як і маркування декларації як модифікаторами, так classі finalдеклараціями.

Джерело: Мова програмування Swift - Тип Властивості змінних


5
Питання задається питанням про "статичний функціонал" та "клас функцій". НЕ запитує про властивості типу. Тож це не дає відповіді на питання - хоча важливо зрозуміти контекст цих ключових слів і щодо властивостей.
эталуз

Ця відповідь просто на неправильне запитання, можливо, вона була розміщена тут випадково?
Fattie

15

Відповідно до книги Swift 2.2, виданої яблуком:

"Ви вказуєте методи введення, записуючи staticключове слово перед ключовим словом методу. Класи можуть також використовувати classключове слово, щоб дозволити підкласам переосмислити реалізацію цього методу надкласовим " .


10

З Swift2.0, Apple каже:

"Визначення вимог до властивостей типу префікса за допомогою статичного ключового слова, коли ви визначаєте їх у протоколі. Це правило стосується навіть того, що вимоги до властивостей типу можуть бути префіксними для класу або статичного ключового слова, коли їх реалізує клас:"


5

Цей приклад очистить кожен аспект!

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
клас SomeClass {class func someTypeMethod () {// реалізація методу типу йде тут}} SomeClass.someTypeMethod ()
Kumar Utsav

Це зовсім не відповідає на питання. Він запитав різницю між ключовими словами staticта classключовими словами.
Doug McBride
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.