【问题标题】:What is the difference between static func and class func in Swift?Swift中的静态函数和类函数有什么区别?
【发布时间】:2014-09-29 03:38:51
【问题描述】:

我可以在 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 用于类和协议吗?是否还有其他需要了解的差异?在语法本身中有这种区别的基本原理是什么?

【问题讨论】:

  • 真的没有区别。我猜他们不能在结构中使用类func,因此是静态函数。 struct func 本来是一个不错的选择。如果你问我,这有点前卫,但好吧,就是这些话。
  • 奖励问题,然后:一个结构可以符合定义class func的协议吗?根据我们现在掌握的信息,这种区分似乎没什么用,不是吗?
  • 是的,你可以。是不是很奇怪?
  • 最大的不同是你可以覆盖class funcs
  • 待考虑:error: class methods are only allowed within classes; use 'static' to declare a static method

标签: class static swift


【解决方案1】:

是不是简单地说,static 用于结构和枚举的静态函数,而 class 用于类和协议?

这是主要区别。其他一些区别是类函数是动态分派的,并且可以被子类覆盖。

协议使用 class 关键字,但它不排除实现协议的结构,它们只是使用 static 代替。为协议选择了类,因此不必使用第三个关键字来表示静态或类。

来自 Chris Lattner 关于这个主题:

我们考虑过统一语法(例如使用“type”作为关键字),但这实际上并不是简单的事情。关键字“类”和“静态”有助于熟悉并且非常具有描述性(一旦您了解 + 方法的工作原理),并为可能向类添加真正的静态方法打开了大门。这个模型的主要奇怪之处在于协议必须选择一个关键字(我们选择了“类”),但总的来说这是正确的权衡。

这里有一个 sn-p,它显示了类函数的一些覆盖行为:

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

【讨论】:

  • 啊哈,非常重要的一点是类函数是动态调度的!但是你能举一个这样的例子吗?您必须在某处写下类名,对吗?那么为什么不静态选择该类的实现呢?
  • 另一个补充问题:你从哪里得到的报价?
  • 我可以在这里提供一个更简单的答案链接吗? stackoverflow.com/questions/29636633/…
  • @Jean-PhilippePellet 在上面的示例中...如果您使用static func myFunc() 而不是class func myFunc,您将收到以下错误l:无法覆盖静态方法。为什么?因为它好像标有final。了解更多信息。请参阅下面的 nextD 的答案。此外,x.dynamicType 现已替换为 type(of:x)
  • dynamically dispatched 是什么意思?
【解决方案2】:

要声明类型变量属性,请使用 static 声明修饰符标记声明。类可以使用 class 声明修饰符标记类型计算属性,以允许子类覆盖超类的实现。类型属性在类型属性中讨论。

注意
在类声明中,关键字static 与使用classfinal 声明修饰符标记声明的效果相同。

来源:The Swift Programming Language - Type Variable Properties

【讨论】:

  • 问题是询问“静态函数”和“类函数”。它不是询问类型属性。因此,这并不能回答问题 - 尽管了解这些关键字在属性方面的上下文也很重要。
  • 这个答案根本是在错误的问题上,可能是不小心在这里发布的?
【解决方案3】:

从 Swift2.0 开始,Apple 说:

“在协议中定义类型属性要求时,始终使用 static 关键字作为前缀。即使类型属性要求在由类实现时可以使用 class 或 static 关键字作为前缀,此规则也适用:”

【讨论】:

    【解决方案4】:

    为了更清楚,我这里举个例子,

    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 funcfinal 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"
      }
    }
    

    【讨论】:

    • 你是冠军,很好的答案..我正在寻找这种差异..杰克!!
    • 完美。令人印象深刻。
    • 这应该被标记为正确答案。干净整洁!
    • 最好的解释!这让我产生了另一个疑问。是否有任何明确的理由使用“类函数”?我的意思是如果你只使用 'func' 它也可以以同样的方式被覆盖,那有什么区别呢?
    • @MarcosReboucas 如果我正确理解了您的问题,class func 与普通的func 不同,尽管两者都可以被覆盖。但是func 是一个实例/对象,class func 可以通过像ClassA.classFunc() 这样的类来访问
    【解决方案5】:

    我在操场上做了一些实验,得到了一些结论。

    TL;DR

    如您所见,在class 的情况下,使用class funcstatic 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."
    

    【讨论】:

    • 您的示例未涵盖在另一个答案中作为主要区别提到的情况:class 函数的动态调度与static 函数的静态绑定。
    • 理解函数的好解释。
    • class func 不是可覆盖的吗?
    • 如果你试图重写一个静态方法,你会得到一个错误。但是,您可以覆盖类方法。查看接受的答案
    • class func 是可覆盖的。否则,我会投票赞成;喜欢研究和例子!
    【解决方案6】:

    根据苹果发布的Swift 2.2 Book:

    “你通过在方法的 func 关键字之前写 static 关键字来指示类型方法。类也可以使用class 关键字允许子类覆盖超类对该方法的实现。”

    【讨论】:

      【解决方案7】:

      这称为类型方法,并使用点语法调用,如实例方法。但是,您在类型上调用类型方法,而不是在该类型的实例上。以下是在名为 SomeClass 的类上调用类型方法的方法:

      【讨论】:

      • class SomeClass { class func someTypeMethod() { // 类型方法实现在这里 } } SomeClass.someTypeMethod()
      • 这根本不能回答问题。他问了staticclass关键字的区别。
      【解决方案8】:

      这个例子将清楚每一个方面!

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

      输出:

      【讨论】:

        【解决方案9】:

        staticclass 关键字都允许我们将方法附加到类而不是类的实例。例如,您可以创建一个具有名称和年龄等属性的 Student 类,然后创建一个静态方法 numberOfStudents,该方法由 Student 类本身而不是单个实例拥有。

        staticclass 的不同之处在于它们支持继承的方式。当你创建一个静态方法时,它被类拥有并且不能被子类更改,而当你使用类时,它可能会在需要时被覆盖。

        这是一个示例代码:

          class Vehicle {
            static func getCurrentSpeed() -> Int {
                return 0
            }
        
            class func getCurrentNumberOfPassengers() -> Int {
                return 0
            } 
        
          }
        
          class Bicycle: Vehicle {
            //This is not allowed
            //Compiler error: "Cannot override static method"
          //  static override func getCurrentSpeed() -> Int {
          //        return 15
          //  }
        
              class override func getCurrentNumberOfPassengers() -> Int {
                return 1
              }
          }
        

        【讨论】:

        • 感谢您的回答,但不确定相对于已经提出和高度投票的答案的附加值......
        猜你喜欢
        • 2014-04-27
        • 2017-08-25
        • 2019-10-02
        • 2017-05-02
        • 2014-03-12
        • 2014-07-30
        • 1970-01-01
        • 2021-12-12
        • 1970-01-01
        相关资源
        最近更新 更多