Penggunaan def, val, dan var dalam scala

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

Garis-garis ini menghasilkan kode 12, meskipun person.age=20telah berhasil dieksekusi. Saya menemukan bahwa ini terjadi karena saya menggunakan def di def person = new Person("Kumar",12). Jika saya menggunakan var atau val outputnya 20. Saya mengerti defaultnya adalah val di scala. Ini:

def age = 30
age = 45

... memberikan kesalahan kompilasi karena ini adalah val secara default. Mengapa rangkaian baris pertama di atas tidak berfungsi dengan baik, namun juga tidak salah?

Byju Veedu
sumber

Jawaban:

254

Ada tiga cara untuk mendefinisikan hal-hal dalam Scala:

  • defmendefinisikan suatu metode
  • valmendefinisikan nilai tetap (yang tidak dapat dimodifikasi)
  • varmendefinisikan variabel (yang dapat dimodifikasi)

Melihat kode Anda:

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

Ini mendefinisikan metode baru yang disebut person. Anda dapat memanggil metode ini hanya tanpa ()karena metode ini didefinisikan sebagai metode tanpa parameter. Untuk metode kosong-paren, Anda dapat memanggilnya dengan atau tanpa '()'. Jika Anda hanya menulis:

person

maka Anda memanggil metode ini (dan jika Anda tidak menetapkan nilai kembali, itu hanya akan dibuang). Di baris kode ini:

person.age = 20

apa yang terjadi adalah bahwa Anda pertama kali memanggil personmetode, dan pada nilai balik (turunan kelas Person) Anda mengubah agevariabel anggota.

Dan baris terakhir:

println(person.age)

Di sini Anda kembali memanggil personmetode, yang mengembalikan instance kelas baru Person(dengan ageset ke 12). Sama seperti ini:

println(person().age)
Jesper
sumber
27
Untuk membingungkan hal-hal, keadaan internal a valdapat diubah tetapi objek yang dirujuk oleh val tidak bisa. A valbukan konstanta.
pferrel
5
Untuk lebih membingungkan hal-hal, val (dan mungkin var juga, saya belum mencobanya) dapat digunakan untuk mendefinisikan suatu fungsi. Ketika menggunakan def untuk mendefinisikan suatu fungsi / metode, tubuh def dievaluasi setiap kali dipanggil. Ketika menggunakan val itu dievaluasi hanya pada titik definisi. Lihat stackoverflow.com/questions/18887264/…
melston
1
@ Melston Ya, tetapi metode dan fungsi juga tidak persis sama .
Jesper
3
untuk lebih membingungkan hal-hal, def juga dapat digunakan untuk mendefinisikan variabel anggota kelas, tidak harus menggunakan var.
Peiti Li
2
@Pferrel tidak terlalu membingungkan. Sama seperti dengan final Java. Anda dapat menandai Listsebagai final, tetapi dapat memodifikasi isinya.
jFrenetic
100

Saya akan mulai dengan perbedaan yang ada di Scala antara def , val dan var .

  • def - mendefinisikan label abadi untuk konten sisi kanan yang malas dievaluasi - mengevaluasi dengan nama.

  • val - mendefinisikan label permanen untuk konten sisi kanan yang bersemangat / segera dievaluasi - dievaluasi oleh nilai.

  • var - mendefinisikan variabel yang dapat diubah , yang awalnya diatur ke konten sisi kanan yang dievaluasi.

Contoh, def

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

Contoh, val

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

Contoh, var

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

scala> aVariable = 5
aVariable: Int = 5

Menurut di atas, label dari def dan val tidak dapat dipindahkan, dan jika ada kesalahan seperti di bawah ini akan muncul:

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

Ketika kelas didefinisikan seperti:

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

dan kemudian dipakai dengan:

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

label yang tidak dapat diubah dibuat untuk instance spesifik Person (yaitu 'personA'). Kapan pun 'usia' bidang yang dapat diubah perlu diubah, upaya tersebut gagal:

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

seperti yang diharapkan, 'usia' adalah bagian dari label yang tidak dapat diubah. Cara yang benar untuk mengerjakan ini terdiri dari menggunakan variabel yang bisa berubah, seperti dalam contoh berikut:

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

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

sejelas itu, dari referensi variabel yang bisa berubah-ubah (yaitu 'personB') dimungkinkan untuk memodifikasi bidang 'kelas' yang bisa berubah-ubah kelas.

Saya masih akan menekankan fakta bahwa semuanya berasal dari perbedaan yang disebutkan di atas, yang harus jelas dalam pikiran setiap programmer Scala.

Paolo Maresca
sumber
Saya kira penjelasan di atas tidak benar. Lihat jawaban lainnya.
Per Mildner
@PerMildner Bisakah Anda jelaskan apa yang salah dalam jawaban di atas?
Syed Souban
Saya tidak ingat keluhan asli saya. Namun, bagian terakhir dari jawabannya, tentang personAet al. sepertinya tidak aktif. Apakah memodifikasi ageanggota berfungsi atau tidak, terlepas dari apakah Anda menggunakan def personAatau tidak var personB. Perbedaannya adalah bahwa dalam def personAkasus Anda memodifikasi Person-instance yang dikembalikan dari evaluasi pertama Anda personA. Mesin virtual ini dimodifikasi, tetapi ini bukan apa yang dikembalikan ketika Anda sekali lagi mengevaluasi personA. Sebagai gantinya, kedua kalinya Anda melakukannya, personA.ageAnda melakukannya dengan efektif new Person("Tim",25).age.
Per Mildner
29

Dengan

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

Anda mendefinisikan variabel fungsi / malas yang selalu mengembalikan contoh Orang baru dengan nama "Kumar" dan usia 12. Ini benar-benar valid dan kompiler tidak punya alasan untuk mengeluh. Memanggil person.age akan mengembalikan usia instance Person yang baru dibuat ini, yang selalu berusia 12.

Saat menulis

person.age = 45

Anda menetapkan nilai baru ke properti umur di kelas Person, yang berlaku sejak usia dinyatakan sebagai var. Kompiler akan mengeluh jika Anda mencoba untuk menetapkan kembali persondengan objek Orang baru seperti

person = new Person("Steve", 13)  // Error
Kintaro
sumber
Iya. Poin ini dapat dengan mudah ditunjukkan dengan memanggil metode kode hash pada personA
Nilanjan Sarkar
26

Untuk memberikan perspektif lain, "def" dalam Scala berarti sesuatu yang akan dievaluasi setiap kali digunakan, sementara val adalah sesuatu yang dievaluasi segera dan hanya sekali . Di sini, ungkapan def person = new Person("Kumar",12)itu menyatakan bahwa setiap kali kita menggunakan "orang" kita akan mendapat new Person("Kumar",12)panggilan. Oleh karena itu wajar bahwa kedua "person.age" tidak berhubungan.

Ini adalah cara saya memahami Scala (mungkin dengan cara yang lebih "fungsional"). Saya tidak yakin jika

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

benar-benar apa yang dimaksudkan Scala. Saya tidak terlalu suka berpikir seperti itu setidaknya ...

xji
sumber
20

Seperti yang sudah dikatakan Kintaro, orang adalah metode (karena def) dan selalu mengembalikan contoh Orang baru. Seperti yang Anda tahu itu akan berhasil jika Anda mengubah metode ke var atau val:

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

Kemungkinan lain adalah:

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

Namun, person.age=20dalam kode Anda diizinkan, saat Anda mendapatkan kembali Personinstance dari personmetode, dan pada contoh ini Anda diizinkan untuk mengubah nilai a var. Masalahnya adalah, bahwa setelah baris itu Anda tidak lagi memiliki referensi ke instance tersebut (karena setiap panggilan personakan menghasilkan instance baru).

Ini tidak istimewa, Anda akan memiliki perilaku yang persis sama di Jawa:

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
Landei
sumber
8

Mari kita ambil ini:

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

dan menulis ulang dengan kode yang setara

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)

Lihat, defadalah metode. Itu akan mengeksekusi setiap kali dipanggil, dan setiap kali itu akan kembali (a) new Person("Kumar", 12). Dan ini bukan kesalahan dalam "tugas" karena itu sebenarnya bukan tugas, tetapi hanya panggilan ke age_=metode (disediakan oleh var).

Daniel C. Sobral
sumber