Scala параметры конструктора классов

Какая разница между:

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

и

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

Могу ли я объявить параметры как var s и изменить их значения позже? Например,

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

  age = happyBirthday(5)

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

Ответ 1

В первой части ответа есть область действия:

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

Если вы префиксные параметры val, var, они будут видны вне класса, в противном случае они будут закрытыми, как вы можете видеть в приведенном выше коде.

И да, вы можете изменить значение var, как обычно.

Ответ 2

Это

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

делает поля доступными для пользователей класса, например. вы можете позже сделать

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

Если вы укажете args как var, то область обзора будет такой же, как для val, но поля изменяемы.

Ответ 3

Если вы знакомы с Java, вы можете получить эту идею из этого примера:

class Person(name: String, age: Int)

похож на

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

Пока

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

похож на

class Person {
  String name;
  int age;

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

Интуиция заключается в том, что без var/val переменная доступна только внутри конструктора. Если var/val добавлен, класс будет иметь переменные-члены с тем же именем.

Ответ 4

Ответы здесь действительно хорошие, однако я занимаюсь этим изучением байтового кода. Когда вы применяете javap в классе, он печатает пакеты, защищенные и общедоступные поля и методы пройденных классов. Я создал класс Person.scala и заполнил его следующим кодом.

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
  }
}

После компиляции кода с помощью scalac Person.scala он генерирует три файла с именами Person.class, PersonVal.calass , PersonVar.cass. Запустив javap для каждого из этих файлов классов, мы увидим, как будет выглядеть эта структура:

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

В этом случае он не создавал никакой переменной класса для Person, поскольку он объявлен ни с val, ни с val, поэтому имя и возраст можно просто использовать внутри конструктора.

>>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);
}

В этом случае он имеет три члена два для конструктора ввода и один для члена, который мы объявили внутри конструктора. Однако у нас нет настроек для конструкторов ввода, поэтому мы не можем изменить значения.

>>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);
}

Он аналогичен примеру PersonVal, но мы можем изменить значения в этом случае с помощью тех методов variable_$eq. это не просто сокращенная версия variable =

Ответ 5

Вы можете использовать case class, и в этом случае класс Person будет иметь эти переменные, доступные вне класса. case class Person(name: String, age: Int). Тогда следующий код будет работать, как ожидалось. val z = new Person("John", 20); z.name //John

Ответ 6

Ответ @Reza, где автор исследует байтовый код с помощью javap, помог мне лучше понять эту концепцию. Чтобы привести очень конкретный пример этого случая, пожалуйста, обратитесь к приведенному ниже сценарию, с которым я столкнулся в своем рабочем веб-приложении (Play + Scala): Как вводить параметры в метод класса/признака в Scala

Если я не использую префикс val для введенного параметра authorizationHandler, тогда компилятор выдает эту ошибку:

  класс MyController должен быть абстрактным, так как авторизация методаHandler в признаке AuthorizationCheck типа = > controllers.authapi.AuthorizationHandler не определен
[error] класс MyController @Inject() (authorizationHandler: AuthorizationHandler) расширяет контроллер с помощью AuthorizationCheck {
[ошибка] ^
[ошибка] обнаружена одна ошибка
Код>

К сожалению, ошибка не помогла мне определить правильную проблему с префиксом val.

  класс MyController @Inject() (авторизация valHandler: AuthorizationHandler) расширяет контроллер с авторизацией Check {
  def myAction = AuthenticatedAction {implicit request = >    ...  }
}
Код>