web-dev-qa-db-ja.com

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
}

パラメーターをvarsとして宣言し、後でそれらの値を変更できますか?例えば、

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

  age = happyBirthday(5)

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

最初の部分の答えはスコープです:

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

パラメーターの前にvalvarを付けると、クラスの外部から見えるようになります。

はい、通常のように変数の値を変更できます。

45
om-nom-nom

この

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

クラスのユーザーが外部でフィールドを利用できるようにします。後でできます

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

Argsをvarとして指定した場合、スコープはvalと同じですが、フィールドは変更可能です。

10
Brian Agnew

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を追加すると、クラスには同じ名前のメンバー変数が含まれます。

8
yxjiang

ここでの答えは本当に良いのですが、バイトコードの調査でこれに取り組んでいます。 javapをクラスに適用すると、渡されたクラスのpackage、protected、およびpublicフィールドとメソッドが出力されます。クラス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という名前の3つのファイルが生成されます。これらの各クラスファイルに対してjavapを実行すると、構造がどのようになるかを確認できます。

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

この場合、valとvalのどちらでも宣言されていないため、Personの変数クラスは作成されませんでした。名前と年齢はコンストラクタ内で使用できます。

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

この場合、2つの入力コンストラクターと、コンストラクター内で宣言したメンバーの3つのメンバーがあります。ただし、入力コンストラクターのセッターがないため、値を変更できません。

>>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 =の単なる短縮版ではありません

2
Reza

著者がjavapを使用してバイトコードを調査する@Rezaの回答は、この概念を最も明確にするのに役立ちました。このケースの非常に具体的な例を引用するには、本番Webアプリ(Play + Scala)で直面した以下のシナリオを参照してください: Scalaのクラス/トレイトメソッドにパラメーターを注入する方法

挿入されたパラメータauthorizationHandlerにvalプレフィックスを使用しない場合、コンパイラは次のエラーをスローします。

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

悲しいことに、このエラーはvalを前に付ける正しい問題を特定するのに役立ちませんでした。

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

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

_case class_を使用できます。その場合、Personクラスにはこれらの変数がクラス外で使用可能になります。 case class Person(name: String, age: Int)。その後、次のコードは期待どおりに機能します。 val z = new Person("John", 20); z.name //John

0
TheM00s3