2010-12-14 45 views
134
class Person(val name:String,var age:Int) 
def person = new Person("Kumar",12) 
person.age = 20 
println(person.age) 

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

def age = 30 
age = 45 

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

Trả lời

29

Với

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

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

Khi viết

person.age = 45 

bạn gán một giá trị mới vào thuộc tính tuổi trong lớp Person, có hiệu lực kể từ 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 phân công lại person với một đối tượng Person mới như

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

Như Kintaro đã nói, người là một phương pháp (vì def) và luôn luôn trả về một trường hợp người mới. Như bạn phát hiện ra nó sẽ có tác dụng nếu bạn thay đổi phương thức để một 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=20 trong mã của bạn được cho phép, khi bạn nhận được trở lại một ví dụ Person từ phương thức person và trong trường hợp này, bạn được phép thay đổi giá trị của var. Vấn đề là, rằng sau dòng đó bạn không có tham chiếu nhiều hơn đến trường hợp đó (như mọi cuộc gọi đến person sẽ tạo ra một cá thể mới).

Đây là 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 
+0

Câu trả lời này là giải thích rõ ràng nhất. – stackoverflowuser2010

221

Có ba cách xác định thứ trong Scala:

  • def định nghĩa một phương pháp
  • val xác định giá trị cố định (không thể sửa đổi được)
  • 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) 

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

person 

thì bạn đang gọi phương thức này (và nếu bạn không chỉ định giá trị trả lại, nó sẽ bị hủy). Trong dòng mã này:

person.age = 20 

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

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

println(person.age) 

Ở đây bạn đang một lần nữa kêu gọi các phương pháp person, mà trả về một thể hiện mới của lớp Person (với age bộ đến 12). Điều này tương tự như thế này:

println(person().age) 
+19

Để làm lẫn lộn mọi thứ, trạng thái bên trong của '' 'val''' có thể được thay đổi nhưng đối tượng được tham chiếu bởi một val không thể. '' 'Val''' không phải là hằng số. – pferrel

+3

Để gây nhầm lẫn nhiều thứ, val (và có thể var là tốt, tôi đã không thử nó) có thể được sử dụng để xác định một chức năng. Khi sử dụng def để định nghĩa một hàm/phương thức, phần thân của def được đánh giá mỗi khi nó được gọi. Khi sử dụng val nó chỉ được đánh giá ở điểm định nghĩa. Xem http://stackoverflow.com/questions/18887264/what-is-the-difference-between-def-and-val-to-define-a-function – melston

+1

@melston Có, nhưng * phương pháp * và * chức năng * cũng không chính xác [cùng một điều] (http://stackoverflow.com/questions/2529184/difference-between-method-and-function-in-scala). – Jesper

8

Hãy thực hiện việc 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, def là một phương pháp. Nó sẽ thực hiện mỗi khi nó được gọi, và mỗi lần nó sẽ trở lại (a) new Person("Kumar", 12). Và đây không phải là lỗi trong "chuyển nhượng" bởi vì nó không thực sự là một nhiệm vụ, nhưng chỉ là một cuộc gọi đến phương pháp age_= (được cung cấp bởi var).

23

Để cung cấp góc độ khác, "def" trong Scala có nghĩa cái gì đó sẽ được đánh giá mỗi lần khi nó được sử dụng, trong khi val là cái gì đó đá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 tôi sử dụng "người", chúng tôi sẽ nhận được một cuộc gọi new Person("Kumar",12). Vì vậy, tự nhiên là hai "person.age" không liên quan.

Đâ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 chắn 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à ý nghĩa của Scala. Tôi thực sự không muốn nghĩ như vậy ít nhất ...

70

Tôi muốn bắt đầu bằng sự khác biệt tồn tại trong Scala giữa def, valvar.

  • def - định nghĩa một bất biến nhãn về nội dung bên phải được uể oải đánh giá - đánh giá theo tên.

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

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

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 trên, nhãn từ defval không thể được bố trí, và trong trường hợp của bất kỳ nỗ lực một lỗi như dưới đây ai sẽ được nâng lên:

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ư sau:

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 

an nhãn không thể thay đổi được tạo cho trường hợp cụ thể đó của Person (tức là 'personA'). Bất cứ khi nào trường có thể thay đổi 'tuổi' cần phải được sửa đổi, nỗ lực đó sẽ không thành công:

như mong đợi, 'tuổi' là một phần của nhãn không thể thay đổi.Các cách chính xác để làm việc về vấn đề này bao gồm trong việc 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 = [email protected] 

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

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

Tôi vẫn nhấn mạnh sự thậ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.

Các vấn đề liên quan