¿Cuál es la diferencia entre función estática y función de clase en Swift?
Puedo ver estas definiciones en la biblioteca Swift:
extension Bool : BooleanLiteralConvertible {
static func convertFromBooleanLiteral(value: Bool) -> Bool
}
protocol BooleanLiteralConvertible {
typealias BooleanLiteralType
class func convertFromBooleanLiteral(value: BooleanLiteralType) -> Self
}
¿Cuál es la diferencia entre una función miembro definida como static func
y otra definida como class func
? ¿Es simplemente static
para funciones estáticas de estructuras y enumeraciones, y class
para clases y protocolos? ¿Existen otras diferencias que debamos conocer? ¿Cuál es la razón para tener esta distinción en la sintaxis misma?
Para ser más claro, pongo un ejemplo aquí,
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
es igual quefinal class func
Debido a que lo es final
, no podemos anularlo en la subclase como se muestra a continuación:
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"
}
}
¿Es simplemente que static es para funciones estáticas de estructuras y enumeraciones, y class para clases y protocolos?
Esa es la principal diferencia. Algunas otras diferencias son que las funciones de clase se distribuyen dinámicamente y las subclases pueden anularlas.
Los protocolos usan la palabra clave class, pero no excluye que las estructuras implementen el protocolo, simplemente usan static en su lugar. Se eligió clase para los protocolos para que no tuviera que haber una tercera palabra clave para representar estática o clase.
De Chris Lattner sobre este tema:
Consideramos unificar la sintaxis (por ejemplo, usar "tipo" como palabra clave), pero en realidad eso no simplifica las cosas. Las palabras clave "clase" y "estática" son buenas para familiarizarse y son bastante descriptivas (una vez que comprenda cómo funcionan los métodos +), y abren la puerta para agregar potencialmente métodos verdaderamente estáticos a las clases. La principal rareza de este modelo es que los protocolos tienen que elegir una palabra clave (y nosotros elegimos "clase"), pero en general es la compensación correcta.
Y aquí hay un fragmento que muestra parte del comportamiento de anulación de las funciones de clase:
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
Hice algunos experimentos en el patio de recreo y saqué algunas conclusiones.
TL;DR
Como puedes ver, en el caso de class
, el uso de class func
o static func
es sólo una cuestión de costumbre.
Ejemplo de parque infantil con explicación:
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."