Scalaでのdef、val、varの使用


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

これらのコード行は12person.age=20正常に実行された場合でも出力されます。これは、でdefを使用したために発生することがわかりましたdef person = new Person("Kumar",12)。varまたはvalを使用すると、出力はになり20ます。私はデフォルトがscalaのvalであることを理解しています。この:

def age = 30
age = 45

...デフォルトではvalであるため、コンパイルエラーが発生します。上記の最初の行が正しく機能せず、エラーも発生しないのはなぜですか?

回答:


254

Scalaで物事を定義するには3つの方法があります。

  • defメソッドを定義します
  • val固定値を定義します(変更できません)
  • var変数を定義します(変更可能)

コードを見る:

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

これにより、という新しいメソッドが定義されpersonます。このメソッド()は、パラメーターなしのメソッドとして定義されているため、呼び出さなくても呼び出すことができます。空のかっこメソッドでは、 '()'を付けても付けなくても呼び出すことができます。あなたが単に書く場合:

person

次に、このメソッドを呼び出します(戻り値を割り当てない場合は、破棄されます)。このコード行では:

person.age = 20

最初にpersonメソッドを呼び出し、戻り値(クラスのインスタンスPerson)でageメンバー変数を変更します。

そして最後の行:

println(person.age)

ここで再びpersonメソッドを呼び出しています。これは、クラスの新しいインスタンスを返しますPersonage12に設定)。これは同じです:

println(person().age)

27
混乱を招くために、aの内部状態はval変更できますが、valによって参照されるオブジェクトは変更できません。A valは定数ではありません。
pferrel 2014

5
さらに混乱させるために、関数を定義するためにval(そして多分varも試したことがありません)を使用できます。defを使用して関数/メソッドを定義する場合、defの本体は、呼び出されるたびに評価されます。valを使用する場合は、定義の時点でのみ評価されます。stackoverflow.com/questions/18887264/…を
メルストン

1
@melstonはい、ただしメソッド関数もまったく同じではありません。
Jesper、2015年

3
さらに混乱させるために、defを使用してクラスのメンバー変数を定義することもできます。必ずしもvarを使用する必要はありません。
Peiti Li 2015

2
@pferrelは本当に混乱しません。Javaのファイナルと同じです。をListとしてマークできますがfinal、その内容を変更できます。
jFrenetic 2017年

100

まず、Scalaに存在するdefvalvarの違いから始めます。

  • DEF -定義不変ラベルされた右側のコンテンツの遅延評価を -名前で評価します。

  • val- すぐに評価される右側のコンテンツの不変のラベルを定義します-値によって評価されます。

  • varが -定義変更可能な変数を最初に評価され、右側のコンテンツに設定され、。

例、def

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

例、val

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

例、var

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

scala> aVariable = 5
aVariable: Int = 5

上記によると、defvalからのラベルは再割り当てできず、何らかの試みがあった場合、以下のようなエラーが発生します:

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

クラスが次のように定義されている場合:

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

次にインスタンス化します:

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

不変のラベルは、人(すなわち「ペルソナ」)の特定のインスタンスのために作成されています。可変フィールド「年齢」を変更する必要がある場合は常に、このような試みは失敗します。

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

予想通り、「年齢」は変更不可能なラベルの一部です。これに対処する正しい方法は、次の例のように、可変変数を使用することです。

scala> var personB = new Person("Matt", 36)
personB: Person = Person@59cd11fe

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

明らかなように、可変変数参照(つまり、「personB」)から、クラスの可変フィールド「age」を変更することが可能です。

私はまだすべてが上記の違いから来るという事実を強調します、それはどんなScalaプログラマーの心にも明確でなければなりません。


上記の説明は正しくないと思います。他の答えを見てください。
ミルドナーによる

@PerMildner上記の回答の何が問題になっているのか詳しく説明していただけますか?
Syed Souban

私の最初の苦情が何であったか覚えていません。しかし、回答の最後の部分について、personAet al。オフのようです。ageメンバーの変更が機能するかどうかは、def personAまたはを使用するかどうかには依存しませんvar personB。違いは、の最初の評価から返されdef personAPerson-instance を変更する場合ですpersonA。このインスタンス変更されます、もう一度評価したときに返されるものではありませんpersonA。代わりに、2回目に実行personA.ageすると、効果的に実行されnew Person("Tim",25).ageます。
ミルドナーによる

29

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

「Kumar」という名前と12歳の新しいPersonインスタンスを常に返す関数/遅延変数を定義しています。これは完全に有効であり、コンパイラーが文句を言う理由はありません。person.ageを呼び出すと、新しく作成されたPersonインスタンスの年齢が返されます。これは常に12です。

書くとき

person.age = 45

Personクラスのageプロパティに新しい値を割り当てます。これは、ageがとして宣言されてvarいるため有効です。次のpersonような新しいPersonオブジェクトで再割り当てしようとすると、コンパイラーは文句を言います。

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

はい。この点は、personAで
Nilanjan Sarkar

26

別の見方をすると、Scalaの「def」は、使用されるたびに評価れるものを意味します、valは、即時かつ1回だけ評価されるものです。ここでの表現def person = new Person("Kumar",12)は、「人」を使用するときはいつでもnew Person("Kumar",12)電話を受けることを伴います。したがって、2つの「person.age」が無関係であることは当然です。

これがScalaを理解する方法です(おそらくより「機能的な」方法で)。わからない

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

Scalaが実際に意図していることはこれだけです。私はそのようなことを少なくとも考えたくありません...


20

Kintaroがすでに述べているように、personは(defのため)メソッドであり、常に新しいPersonインスタンスを返します。ご存知のように、メソッドをvarまたはvalに変更すると機能します。

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

別の可能性は:

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

ただし、メソッドからインスタンスperson.age=20を取得するとコード内で許可され、このインスタンスではの値を変更できます。問題は、その行の後はそのインスタンスへの参照がなくなるということです(を呼び出すたびに新しいインスタンスが生成されるため)。Personpersonvarperson

これは特別なことではなく、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

8

これを見てみましょう:

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

同等のコードで書き直してください

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)

参照は、defメソッドです。呼び出されるたびに実行され、返されるたびに(a)を返しnew Person("Kumar", 12)ます。そして、これらは実際には割り当てではなく、age_=メソッドへの呼び出し(によって提供されるvar)なので、「割り当て」のエラーではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.