2010-12-14 58 views
134
class Person(val name:String,var age:Int) 
def person = new Person("Kumar",12) 
person.age = 20 
println(person.age) 

Ces lignes de sorties de code 12, même si person.age=20 a été exécuté avec succès. J'ai trouvé que cela arrive parce que j'ai utilisé def dans def person = new Person("Kumar",12). Si j'utilise var ou val, la sortie est 20. Je comprends que la valeur par défaut est val en scala. Ceci:utilisation de def, val et var dans scala

def age = 30 
age = 45 

... donne une erreur de compilation parce que c'est un val par défaut. Pourquoi le premier ensemble de lignes ci-dessus ne fonctionne pas correctement, et pourtant ne pas d'erreur?

Répondre

29

Avec

def person = new Person("Kumar", 12) 

vous définissez une fonction/variable de paresseux qui retourne toujours une nouvelle instance de personne avec le nom « Kumar » et 12 ans ce qui est totalement valide et le compilateur n'a aucune raison de se plaindre. L'appel person.age retournera l'âge de cette nouvelle instance personne, qui est toujours 12.

Lors de l'écriture

person.age = 45 

vous attribuez une nouvelle valeur à la propriété d'âge en personne de classe, ce qui est valable depuis l'âge est déclaré comme var. Le compilateur se plaindra si vous essayez de réassigner person avec un nouvel objet Personne comme

person = new Person("Steve", 13) // Error 
18

Comme Kintaro dit déjà, personne est une méthode (à cause de def) et renvoie toujours une nouvelle instance de personne. Comme vous l'avez découvert cela fonctionnerait si vous changez la méthode à un var ou val:

val person = new Person("Kumar",12) 

Une autre possibilité serait:

def person = new Person("Kumar",12) 
val p = person 
p.age=20 
println(p.age) 

Cependant, person.age=20 dans votre code est autorisé, que vous revenez une instance Person de la méthode person, et sur cette instance, vous êtes autorisé à changer la valeur d'un var. Le problème est qu'après cette ligne, vous n'avez plus de référence à cette instance (car chaque appel à person produira une nouvelle instance).

Ceci est rien de spécial, vous auriez exactement le même comportement en Java:

class Person{ 
    public int age; 
    private String name; 
    public Person(String name; int age) { 
     this.name = name; 
     this.age = age; 
    } 
    public String name(){ return name; } 
} 

public Person person() { 
    return new Person("Kumar", 12); 
} 

person().age = 20; 
System.out.println(person().age); //--> 12 
+0

Cette réponse est l'explication la plus claire. – stackoverflowuser2010

221

Il y a trois façons de définir les choses à Scala:

  • def définit une méthode
  • val définit une valeur fixe (qui ne peut pas être modifiée)
  • var définit une variable (qui peut être modifié)

En regardant votre code:

def person = new Person("Kumar",12) 

Cela définit une nouvelle méthode appelée person. Vous pouvez appeler cette méthode uniquement sans () car elle est définie comme méthode sans paramètre. Pour la méthode empty-paren, vous pouvez l'appeler avec ou sans '()'. Si vous écrivez simplement:

person 

vous appelez cette méthode (et si vous ne donnez pas la valeur de retour, il sera tout simplement être mis au rebut). Dans cette ligne de code:

person.age = 20 

ce qui se passe est que vous appelez d'abord la méthode person, et sur la valeur de retour (une instance de la classe Person) vous changez la variable membre age.

Et la dernière ligne:

println(person.age) 

Ici vous appellent à nouveau la méthode person, qui retourne une nouvelle instance de la classe Person (avec age ensemble à 12). Il est comme ceci:

println(person().age) 
+19

Pour confondre les choses, l'état interne d'un '' '' '' '' ''''' peut être changé mais l'objet référencé par un val ne peut pas. Un '' '' val''' n'est pas une constante. – pferrel

+3

Pour compliquer davantage les choses, val (et peut-être aussi var, je ne l'ai pas essayé) peut être utilisé pour définir une fonction. Lors de l'utilisation de def pour définir une fonction/méthode, le corps du def est évalué chaque fois qu'il est appelé. Lors de l'utilisation val, il est évalué uniquement au moment de la définition. Voir http://stackoverflow.com/questions/18887264/what-is-the-difference-between-def-and-val-to-define-a-function – melston

+1

@melston Oui, mais une * méthode * et une * fonction * ne sont également pas exactement [la même chose] (http://stackoverflow.com/questions/2529184/difference-between-method-and-function-in-scala). – Jesper

8

Jetons ceci:

class Person(val name:String,var age:Int) 
def person =new Person("Kumar",12) 
person.age=20 
println(person.age) 

et récrire avec code équivalent

class Person(val name:String,var age:Int) 
def person =new Person("Kumar",12) 
(new Person("Kumar", 12)).age_=(20) 
println((new Person("Kumar", 12)).age) 

Voir, def est une méthode. Il s'exécutera chaque fois qu'il est appelé, et chaque fois il retournera (a) new Person("Kumar", 12). Et ce n'est pas une erreur dans la "cession", car ce n'est pas vraiment une affectation, mais juste un appel à la méthode age_= (fournie par var).

23

Fournir une autre perspective, « def » dans Scala signifie quelque chose qui va être évalué chaque fois quand il est utilisé, alors que val est quelque chose qui est évalué immédiatement et une seule fois. Ici, l'expression def person = new Person("Kumar",12) implique que chaque fois que nous utilisons "personne", nous recevrons un appel new Person("Kumar",12). Il est donc naturel que les deux "person.age" ne soient pas liés.

C'est la façon dont je comprends Scala (probablement d'une manière plus « fonctionnelle »). Je ne sais pas si

def defines a method 
val defines a fixed value (which cannot be modified) 
var defines a variable (which can be modified) 

est vraiment ce que Scala entend dire si. Je n'aime pas vraiment penser de cette façon au moins ...

70

Je commencerai par la distinction qui existe entre dans Scala def, val et var.

  • def - définit une étiquette immuable pour le contenu latéral droit qui est évalué paresseusement - évaluer par nom.

  • val - définit une étiquette immuable pour le contenu de côté droit qui est vivement /immédiatement évalué - évalué par valeur.

  • var - définit une variables mutables, initialement fixé au contenu du côté droit évalué.

exemple, def

scala> def something = 2 + 3 * 4 
something: Int 
scala> something // now it's evaluated, lazily upon usage 
res30: Int = 14 

exemple, val

scala> val somethingelse = 2 + 3 * 5 // it's evaluated, eagerly upon definition 
somethingelse: Int = 17 

exemple, var

scala> var aVariable = 2 * 3 
aVariable: Int = 6 

scala> aVariable = 5 
aVariable: Int = 5 

Selon ci-dessus, les étiquettes de def et val ne peuvent pas être réaffectés, et en cas de toute tentative une erreur comme ci-dessous un ressusciteront:

scala> something = 5 * 6 
<console>:8: error: value something_= is not a member of object $iw 
     something = 5 * 6 
    ^

Lorsque la classe est définie comme:

scala> class Person(val name: String, var age: Int) 
defined class Person 

puis instanciée:

scala> def personA = new Person("Tim", 25) 
personA: Person 

une étiquette immuable est créé pour cette instance spécifique de la personne (à savoir 'personnage'). Chaque fois que le champ mutable « âge » doit être modifié, cette tentative échoue:

scala> personA.age = 44 
personA.age: Int = 25 

comme prévu, « âge » fait partie d'une étiquette non-mutables.La bonne façon de travailler sur ce consiste à utiliser une variable mutable, comme dans l'exemple suivant:

scala> var personB = new Person("Matt", 36) 
personB: Person = [email protected] 

scala> personB.age = 44 
personB.age: Int = 44 // value re-assigned, as expected 

aussi claire, de la variables mutables référence (ie « personB ») il est possible de modifier la classe champ mutable 'age'.

Je voudrais encore souligner le fait que tout vient de la différence mentionnée ci-dessus, qui doit être clair à l'esprit de tout programmeur Scala.