Sử dụng def, val và var trong scala


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

Những dòng đầu ra mã này 12, mặc dù person.age=20đã được thực hiện thành công. Tôi thấy rằng điều này xảy ra bởi vì tôi đã sử dụng def in def person = new Person("Kumar",12). Nếu tôi sử dụng var hoặc val thì đầu ra là 20. Tôi hiểu mặc định là val trong scala. Điều này:

def age = 30
age = 45

... đưa ra lỗi biên dịch vì nó là một giá trị theo mặc định. Tại sao bộ dòng đầu tiên ở trên không hoạt động chính xác và cũng không bị lỗi?

Câu trả lời:


254

Có ba cách định nghĩa mọi thứ trong Scala:

  • defđịnh nghĩa một phương thức
  • valđịnh nghĩa một giá trị cố định (không thể sửa đổi)
  • varđịnh nghĩa một biến (có thể được sửa đổi)

Nhìn vào mã của bạn:

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

Điều này định nghĩa một phương thức mới được gọi là person. Bạn chỉ có thể gọi phương thức này mà không ()được định nghĩa là phương thức không tham số. Đối với phương thức paren rỗng, bạn có thể gọi nó bằng hoặc không có '()'. Nếu bạn chỉ đơn giản viết:

person

sau đó bạn đang gọi phương thức này (và nếu bạn không gán giá trị trả về, nó sẽ bị loại bỏ). Trong dòng mã này:

person.age = 20

Điều gì xảy ra là trước tiên bạn gọi personphương thức và trên giá trị trả về (một thể hiện của lớp Person), bạn đang thay đổi agebiến thành viên.

Và dòng cuối cùng:

println(person.age)

Ở đây bạn lại gọi personphương thức, nó trả về một thể hiện mới của lớp Person( ageđược đặt thành 12). Nó giống như thế này:

println(person().age)

27
Để gây nhầm lẫn, trạng thái bên trong của a valcó thể được thay đổi nhưng đối tượng được gọi bằng val không thể. A valkhông phải là hằng số.
pferrel

5
Để gây nhầm lẫn hơn nữa, val (và có thể cả var, tôi đã không thử nó) có thể được sử dụng để xác định hàm. Khi sử dụng def để định nghĩa một hàm / phương thức, phần thân của def được ước tính mỗi khi nó được gọi. Khi sử dụng val, nó chỉ được đánh giá tại điểm định nghĩa. Xem stackoverflow.com/questions/18887264/ từ
melston 17/05/2015

1
@melston Có, nhưng một phương thức và một chức năng cũng không hoàn toàn giống nhau .
Jesper

3
để thậm chí gây nhầm lẫn hơn nữa, def cũng có thể được sử dụng để xác định các biến thành viên của một lớp, không nhất thiết phải sử dụng var.
Peiti Li

2
@pferrel không thực sự khó hiểu. Giống như với trận chung kết của Java. Bạn có thể đánh dấu Listfinal, nhưng có thể sửa đổi nội dung của nó.
jFrenetic

100

Tôi sẽ bắt đầu bằng sự phân biệt tồn tại trong Scala giữa def , valvar .

  • def - định nghĩa một nhãn bất biến cho nội dung bên phải được đánh giá một cách lười biếng - đánh giá theo tên.

  • val - định nghĩa một nhãn bất biến cho nội dung bên phải được đánh giá một cách háo hức / ngay lập tức - được đánh giá theo giá trị.

  • var - định nghĩa một biến có thể thay đổi , ban đầu được đặt thành nội dung bên phải được đánh giá.

Ví dụ: def

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

Ví dụ, val

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

Ví dụ, var

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

scala> aVariable = 5
aVariable: Int = 5

Theo như trên, các nhãn từ defval không thể được chỉ định lại và trong trường hợp có bất kỳ lỗi nào như lỗi dưới đây sẽ được đưa ra:

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

Khi lớp được định nghĩa như:

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

và sau đó khởi tạo với:

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

một nhãn bất biến được tạo cho trường hợp cụ thể của Người đó (tức là 'personA'). Bất cứ khi nào trường thay đổi 'tuổi' cần được sửa đổi, lần thử đó không thành công:

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

như mong đợi, 'tuổi' là một phần của nhãn không thể thay đổi. Cách chính xác để làm việc này bao gồm sử dụng một biến có thể thay đổi, như trong ví dụ sau:

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

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

như rõ ràng, từ tham chiếu biến có thể thay đổi (tức là 'personB'), có thể sửa đổi trường 'tuổi' có thể thay đổi '.

Tôi vẫn sẽ nhấn mạnh thực tế rằng mọi thứ xuất phát từ sự khác biệt đã nêu ở trên, điều đó phải rõ ràng trong tâm trí của bất kỳ lập trình viên Scala nào.


Tôi không nghĩ rằng lời giải thích trên là chính xác. Xem các câu trả lời khác.
Per Mildner

@PerMildner Bạn có thể giải thích rõ những gì sai trong câu trả lời trên không?
Syed Souban

Tôi không nhớ khiếu nại ban đầu của mình là gì. Tuy nhiên, phần cuối của câu trả lời, về personAet al. dường như tắt Việc sửa đổi agethành viên có hoạt động hay không là độc lập với việc bạn sử dụng def personAhay var personB. Sự khác biệt là trong def personAtrường hợp bạn đang sửa đổi Person-instance được trả về từ đánh giá đầu tiên của bạn personA. Trường hợp này được sửa đổi, nhưng nó không phải là những gì được trả lại khi bạn một lần nữa đánh giá personA. Thay vào đó, lần thứ hai bạn làm personA.agebạn đang làm một cách hiệu quả new Person("Tim",25).age.
Per Mildner

29

Với

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

bạn đang xác định một hàm / biến lười biếng luôn trả về một cá thể Person mới có tên "Kumar" và tuổi 12. Điều này hoàn toàn hợp lệ và trình biên dịch không có lý do gì để phàn nàn. Calling person.age sẽ trả về tuổi của cá thể Person mới được tạo này, luôn là 12.

Khi viết

person.age = 45

bạn gán một giá trị mới cho thuộc tính tuổi trong lớp Người, có giá trị vì tuổi được khai báo là var. Trình biên dịch sẽ phàn nàn nếu bạn cố gắng gán lại personvới một đối tượng Person mới như

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

Đúng. Điểm này có thể dễ dàng được chứng minh bằng cách gọi phương thức hashCode trên ngườiA
Nilanjan Sarkar

26

Để cung cấp một viễn cảnh khác, "def" trong Scala có nghĩa là thứ gì đó sẽ được đánh giá mỗi khi nó được sử dụng, trong khi val là thứ được đánh giá ngay lập tức và chỉ một lần . Ở đây, biểu thức def person = new Person("Kumar",12)đòi hỏi rằng bất cứ khi nào chúng ta sử dụng "người", chúng ta sẽ nhận được một new Person("Kumar",12)cuộc gọi. Do đó, điều tự nhiên là hai "person.age" không liên quan đến nhau.

Đây là cách tôi hiểu Scala (có thể theo cách "chức năng" hơn). Tôi không chắc nếu

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

thực sự là những gì Scala dự định có nghĩa là mặc dù. Tôi thực sự không muốn nghĩ theo cách đó ít nhất ...


20

Như Kintaro đã nói, person là một phương thức (vì def) và luôn trả về một thể hiện Person mới. Khi bạn phát hiện ra nó sẽ hoạt động nếu bạn thay đổi phương thức thành var hoặc val:

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

Một khả năng khác sẽ là:

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

Tuy nhiên, person.age=20trong mã của bạn được cho phép, khi bạn lấy lại một Personthể hiện từ personphương thức và trong trường hợp này, bạn được phép thay đổi giá trị của a var. Vấn đề là, sau dòng đó, bạn không còn tham chiếu đến thể hiện đó nữa (vì mỗi lệnh gọi personsẽ tạo ra một thể hiện mới).

Điều này không có gì đặc biệt, bạn sẽ có chính xác hành vi tương tự trong 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

Hãy lấy cái này:

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

và viết lại nó với mã tương đương

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)

Xem, deflà một phương pháp. Nó sẽ thực thi mỗi lần nó được gọi và mỗi lần nó sẽ trả về (a) new Person("Kumar", 12). Và đây không phải là lỗi trong "bài tập" bởi vì nó không thực sự là một bài tập, mà chỉ là một cuộc gọi đến age_=phương thức (được cung cấp bởi var).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.