違いは何ですか:
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
}
}
最初の部分の答えはスコープです:
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
を付けると、クラスの外部から見えるようになります。
はい、通常のように変数の値を変更できます。
この
class Person(val name: String, val age: Int)
クラスのユーザーが外部でフィールドを利用できるようにします。後でできます
val p = new Person("Bob", 23)
val n = p.name
Argsをvar
として指定した場合、スコープはval
と同じですが、フィールドは変更可能です。
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を追加すると、クラスには同じ名前のメンバー変数が含まれます。
ここでの答えは本当に良いのですが、バイトコードの調査でこれに取り組んでいます。 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 =
の単なる短縮版ではありません
著者が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 =>
...
}
}
_case class
_を使用できます。その場合、Person
クラスにはこれらの変数がクラス外で使用可能になります。 case class Person(name: String, age: Int)
。その後、次のコードは期待どおりに機能します。 val z = new Person("John", 20); z.name //John