web-dev-qa-db-fra.com

paramètres du constructeur scala

Quelle est la différence entre:

class Person(name: String, age: Int) {
  def say = "My name is " + name + ", age " + age
}

et

class Person(val name: String, val age: Int) { 
  def say = "My name is " + name + ", age " + age
}

Puis-je déclarer des paramètres en tant que vars et modifier leurs valeurs ultérieurement? Par exemple,

class Person(var name: String, var age: Int) {

  age = happyBirthday(5)

  def happyBirthday(n: Int) {
    println("happy " + n + " birthday")
    n
  }
}
42
zihaoyu

Pour la première partie, la réponse est portée: 

scala> class Person(name: String, age: Int) {
     |   def say = "My name is " + name + ", age " + age
     | }

scala> val x = new Person("Hitman", 40)

scala> x.name
<console>:10: error: value name is not a member of Person
              x.name

Si vous préfixez les paramètres avec val, var, ils seront visibles de l'extérieur de la classe, sinon ils seront privés, comme vous pouvez le voir dans le code ci-dessus.

Et oui, vous pouvez changer la valeur de la variable, comme d'habitude.

43
om-nom-nom

Ce

class Person(val name: String, val age: Int)

rend les champs disponibles en externe pour les utilisateurs de la classe, par ex. tu peux faire plus tard

val p = new Person("Bob", 23)
val n = p.name

Si vous spécifiez les arguments sous la forme var, la portée est la même que pour val, mais les champs sont mutables.

10
Brian Agnew

Si vous connaissez Java, vous pouvez vous faire une idée à partir de cet exemple:

class Person(name: String, age: Int)

est semblable à

class Person {
  public Person(String name, int age) {
  }
}

Tandis que 

class Person(var name: String, var age: Int) // also we can use 'val'

est similaire à

class Person {
  String name;
  int age;

  public Person(String name, int age) {
     this.name = name;
     this.age = age;
  }
}

L'intuition est que sans var/val, la variable n'est accessible que dans le constructeur. Si var/val est ajouté, la classe aura les variables membres avec le même nom.

8
yxjiang

Les réponses ici sont vraiment bonnes, mais je m'attaque à celui-ci en explorant le code d'octet. Lorsque vous appliquez javap sur une classe, celle-ci imprime les champs de package, protégés et publics ainsi que les méthodes des classes transmises. J'ai créé une classe Person.scala et l'ai complétée avec le code suivant.

class Person(name: String, age: Int) {
  def say = "My name is " + name + ", age " + age
}

class PersonVal(val name: String, val age: Int) {
  def say = "My name is " + name + ", age " + age
}

class PersonVar(var name: String, var age: Int) {

  age = happyBirthday(5)

  def happyBirthday(n: Int) = {
    println("happy " + n + " birthday")
    n
  }
}

Après avoir compilé le code avec scalac Person.scala, trois fichiers portant le nom Person.class, PersonVal.calass , PersonVar.cass sont générés. En exécutant javap pour chacun de ces fichiers de classe, nous pouvons voir comment la structure serait:

>>javap Person.class
Compiled from "Person.scala"
public class Person {
  public Java.lang.String say();
  public Person(Java.lang.String, int);
}

Dans ce cas, il n'a créé aucune classe variable pour Person car il est déclaré avec ni val, ni val, donc name et age peuvent uniquement être utilisés dans le constructeur. 

>>javap PersonVal.class
public class PersonVal {
  public Java.lang.String name();
  public int age();
  public Java.lang.String say();
  public PersonVal(Java.lang.String, int);
}

Dans ce cas, il a trois membres, deux pour le constructeur en entrée et un pour le membre que nous avons déclaré dans le constructeur. Cependant, nous n'avons pas de setter pour les constructeurs d'entrée, nous ne pouvons donc pas changer les valeurs. 

>>javap PersonVar.class
public class PersonVar {
  public Java.lang.String name();
  public void name_$eq(Java.lang.String);
  public int age();
  public void age_$eq(int);
  public int happyBirthday(int);
  public PersonVar(Java.lang.String, int);
}

C'est la même chose que l'exemple PersonVal mais nous pouvons changer les valeurs dans ce cas avec ces méthodes variable_$eq. il n'y a rien qu'une version abrégée de variable = 

2
Reza

La réponse de @Reza où l'auteur explore le code d'octet à l'aide de javap m'a aidé à clarifier ce concept au mieux. Pour citer un exemple très spécifique de ce cas, veuillez vous reporter au scénario ci-dessous que j'ai rencontré dans mon application de production Web (Play + Scala): Comment injecter des paramètres dans une méthode de classe/trait dans Scala

Si je n'utilise pas le préfixe val pour injecter le paramètre authorizationHandler, alors Compiler lève cette erreur:

class MyController needs to be abstract, since method authorizationHandler in trait AuthorizationCheck of type => controllers.authapi.AuthorizationHandler is not defined
[error] class MyController @Inject() (authorizationHandler: AuthorizationHandler) extends Controller with AuthorizationCheck {
[error]       ^
[error] one error found

Malheureusement, l'erreur ne m'a pas aidé à identifier le problème correct, à savoir préfixer avec val.

class MyController @Inject()(val authorizationHandler: AuthorizationHandler) extends Controller with AuthorizationCheck {

   def myAction = AuthenticatedAction { implicit request =>
     ...
   }
} 
0
NKM