Qual é a diferença entre func estático e func de classe no Swift?

334

Eu posso ver essas definições na biblioteca Swift:

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

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

Qual é a diferença entre uma função membro definida como static funce outra definida como class func? Simplesmente staticé para funções estáticas de estruturas e enumerações, e classpara classes e protocolos? Existem outras diferenças que alguém deva conhecer? Qual é a justificativa para ter essa distinção na própria sintaxe?

Jean-Philippe Pellet
fonte
3
Realmente não há diferença. Eles não podiam usar a classe func em uma estrutura, eu acho, portanto, estática. struct func teria sido um bom candidato. Isso é um pouco nervoso se você me perguntar, mas bem, essas são as palavras.
fabrice truillot de chambrier
2
Pergunta de bônus, então: uma estrutura pode estar em conformidade com um protocolo que define a class func? Com as informações que temos agora, essa distinção parece bastante inútil, não é?
Jean-Philippe Pellet
3
sim você pode. Estranho, não é?
fabrice truillot de chambrier
7
a grande diferença é que você pode substituir class funcs
Fattie
11
A ser considerado:error: class methods are only allowed within classes; use 'static' to declare a static method
Gabriel Goncalves

Respostas:

238

Simplesmente estático é para funções estáticas de estruturas e enumerações e classe para classes e protocolos?

Essa é a principal diferença. Algumas outras diferenças são que as funções de classe são despachadas dinamicamente e podem ser substituídas por subclasses.

Os protocolos usam a palavra-chave class, mas não exclui estruturas da implementação do protocolo, apenas usam estática. A classe foi escolhida para protocolos, para que não houvesse uma terceira palavra-chave para representar estática ou classe.

De Chris Lattner sobre este tópico:

Consideramos unificar a sintaxe (por exemplo, usar "type" como palavra-chave), mas isso não é simplesmente coisas. As palavras-chave "classe" e "estático" são boas para familiaridade e são bastante descritivas (depois de entender como os métodos + funcionam) e abrem a porta para potencialmente adicionar métodos realmente estáticos às classes. A principal estranheza desse modelo é que os protocolos precisam escolher uma palavra-chave (e nós escolhemos "classe"), mas, no geral, é a troca certa.

E aqui está um trecho que mostra alguns dos comportamentos de substituição das funções de classe:

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
fonte
4
Ah, ponto muito importante que as funções de classe são despachadas dinamicamente! Mas você poderia dar um exemplo? Você teria que escrever o nome da turma em algum lugar, certo? Então, por que não escolher estaticamente a implementação dessa classe?
Jean-Philippe Pellet
11
Outra pergunta complementar: de onde você tirou a cotação?
Jean-Philippe Pellet
meu entendimento é que as funções de classe trabalhar praticamente o mesmo que objc + métodos sob o capô
Connor
11
Posso fornecer um link de resposta mais simples aqui? stackoverflow.com/questions/29636633/…
allenlinli
11
@ Jean-PhilippePellet No exemplo acima ... se você usar em static func myFunc()vez de class func myFunc, receberá o seguinte erro l: não é possível substituir o método estático . Por quê? Porque é como se tivesse sido marcado com final. Para maiores informações. Veja a resposta do nextD abaixo. Também x.dynamicTypeagora foi substituído portype(of:x)
Honey
246

Para ser mais claro, eu faço um exemplo aqui,

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 é o mesmo que final class func

Porque é final, não podemos substituí-lo na subclasse como abaixo:

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
fonte
18
você campeão, ótima resposta .. eu estava procurando essa diferença .. Jake !!
Abhimanyu Rathore
5
Perfeito. Impressionante.
Mehul #
5
Isso deve ser marcado como a resposta correta. Arrumado e limpo!
precisa saber é o seguinte
11
Melhor explicação! Isso me levou a outra dúvida. Existe algum motivo explícito para usar uma 'classe func'? Quero dizer, se você apenas usar 'func', também poderá ser substituído da mesma maneira, então qual é a diferença?
Marcos Reboucas
11
@MarcosReboucas, se eu entendi sua pergunta corretamente, class funcé diferente do normal, funcembora ambos possam ser substituídos. Mas funcé para uma instância / objeto e class funcpode ser acessado através da classe comoClassA.classFunc()
Jake Lin
78

Fiz algumas experiências no parquinho e tirei algumas conclusões.

TL; DR insira a descrição da imagem aqui

Como você pode ver, no caso de class, o uso class funcou static funcé apenas uma questão de hábito.

Exemplo de playground com explicação:

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
fonte
7
Seus exemplos não cobrem o caso mencionado como principal diferença em outra resposta: despacho dinâmico de classfunções vs. vinculação estática de um static.
Jean-Philippe Pellet
11
Ótima explicação para entender as funções.
Yucel Bayram
33
Não é class funcsubstituível?
Iulian Onofrei 10/11
9
Se você tentar substituir um método estático, receberá um erro. No entanto, você pode substituir um método de classe. Veja a resposta aceita
querida
8
class funcé substituível. Eu teria votado nisso de outra maneira; ame a pesquisa e o exemplo!
precisa saber é o seguinte
52

Para declarar uma propriedade de variável de tipo, marque a declaração com o staticmodificador de declaração. As classes podem marcar as propriedades computadas do tipo com o classmodificador de declaração, para permitir que as subclasses substituam a implementação da superclasse. As propriedades de tipo são discutidas em Propriedades de tipo.

NOTA
Em uma declaração de classe, a palavra static- chave tem o mesmo efeito que marcar a declaração com os modificadores de declaração classe final.

Fonte: A linguagem de programação Swift - Propriedades da variável de tipo

NexD.
fonte
5
A pergunta é sobre 'static func' e 'class func'. NÃO está perguntando sobre propriedades de tipo. Portanto, isso não responde à pergunta - embora seja importante entender o contexto dessas palavras-chave em relação às propriedades.
precisa saber é o seguinte
Esta resposta está simplesmente na pergunta errada, talvez tenha sido postada aqui acidentalmente?
Fattie 5/03
15

De acordo com o Swift 2.2 Book publicado pela apple:

“Você indica métodos de texto escrevendo a staticpalavra - chave antes da palavra-chave func do método. As classes também podem usar a classpalavra - chave para permitir que as subclasses substituam a implementação desse método pela superclasse . ”

Milad
fonte
10

A partir do Swift2.0, a Apple diz:

"Sempre prefixe os requisitos de propriedade de tipo com a palavra-chave estática quando você os definir em um protocolo. Esta regra se aplica mesmo que os requisitos de propriedade de tipo possam ser prefixados com a classe ou a palavra-chave estática quando implementada por uma classe:"

Jacky
fonte
4

Este exemplo irá limpar todos os aspectos!

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

Resultado: Saída de todos os tipos de função

Rehan Ali Khan
fonte
-6

Isso é chamado de métodos de tipo e é chamado com sintaxe de ponto, como métodos de instância. No entanto, você chama métodos de tipo no tipo, não em uma instância desse tipo. Veja como você chama um método de tipo em uma classe chamada SomeClass:

Kumar Utsav
fonte
11
classe SomeClass {someTypeMethod classe func () {// implementação do método tipo vai aqui}} SomeClass.someTypeMethod ()
Kumar Utsav
Isso não responde à pergunta. Ele perguntou a diferença entre statice classpalavras - chave.
Doug McBride