2009-09-08 4 views
16

Scala Je peux le faire:Scala remplaçant une définition non abstraite avec un var

trait SomeTrait { 
    protected def foo: String 
} 

class Wibble extends SomeTrait { 
    protected var foo = "Hello" 
} 

Mais je ne peux pas faire la même chose où je donne une définition par défaut pour foo

trait SomeTrait { 
    protected def foo: String = "World" 
} 

class Wibble extends SomeTrait { 
    protected var foo = "Hello" //complains about lack of override modifier 

    override protected var foo = "Hello" //complains "method foo_ overrides nothing" 
} 

Pourquoi je ne peux pas faire ça?

EDIT: Après une conversation sur les utilisateurs scala liste de diffusion, je raised this in trac

Répondre

19

En Scala, lorsque vous écrivez un var foo, le compilateur Scala génère automatiquement un setter (appelé foo_=) et un getter (appelé foo) et définit le champ comme privé (vous le verrez comme privé si vous décompilez une classe ayant des champs Scala 'publics' avec javap). C'est ce que signifie l'erreur 'method foo_ = overrides nothing'. Dans votre trait, vous n'avez pas défini une méthode foo_=, et pour un accesseur de champ public et des getters toujours viennent par paires.

Si vous ne fournissez pas de valeur par défaut dans le trait (c'est-à-dire la méthode abstraite), le mot-clé override n'est pas nécessaire. Par conséquent, dans votre premier exemple, le getter remplace la méthode abstraite et le setter ... il est juste là. Le compilateur ne se plaint pas. Mais lorsque vous fournissez une implémentation réelle de la méthode dans le trait, vous devez écrire spécifiquement le mot-clé override lors du remplacement. Lorsque vous écrivez protected var foo, vous n'avez pas spécifié le mot-clé override pour le getter et lorsque vous écrivez override protected var foo, vous avez également indiqué au compilateur que la méthode foo_= doit être remplacée, mais le trait n'a pas cette méthode.

Aussi, logiquement, vous ne pouvez pas vraiment surcharger un def avec un var (en considérant une vue stricte de dérogation, comme dans le paragraphe précédent). Une def est logiquement une fonction (vous lui donnez une entrée, elle produit une sortie). Un var est similaire à une fonction sans argument, mais prend également en charge la définition de sa valeur à autre chose, opération qui n'est pas prise en charge par une fonction. Au lieu de cela, si vous le changez en val, ce serait OK. C'est comme une fonction qui produit toujours le même résultat (en cache).

Si vous voulez avoir un comportement similaire à un var que vous pourriez faire quelque chose comme ça (en ayant setter explicite et getters):

class Wibble extends SomeTrait { 
    private var bar = "Hello" 
    override protected def foo = bar 
    protected def foo_=(v: String) { bar = v} 
} 

Maintenant, vous pouvez faire tout ce que vous pourriez faire avec un :) var.

val x = new Wibble 
println(x.foo) // yields "Hello" 
x.foo = "Hello again" 
println(x.foo) // yields "Hello again" 
+0

Désolé - j'ai fait une erreur - la méthode trait originale aurait dû être protégée aussi. Je ne suis pas sûr d'être d'accord avec vous sur l'impossibilité logique de remplacer un 'def' par un' var'. Tout ce que 'def' dit est que je m'attends à ce qu'il y ait un accesseur appelé' foo' qui retourne une chaîne - déclarant un 'var' est une implémentation de ceci –

+0

Je pense que je suis d'accord avec vous. Je pensais à dépasser comme une notion plus stricte quand j'ai écrit la réponse. Je vais éditer la réponse, et fournir une solution réalisable :). –

+0

Dommage qu'il n'y ait pas de syntaxe 'override def as var ' –

Questions connexes