ใช้ def, val และ var ในสกาล่า


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

บรรทัดของรหัสผลลัพธ์เหล่านี้12แม้ว่าจะperson.age=20ถูกดำเนินการเรียบร้อยแล้ว ผมพบว่าเรื่องนี้เกิดขึ้นเพราะผมใช้ def def person = new Person("Kumar",12)ใน หากฉันใช้ var หรือ Val 20ออกเป็น ฉันเข้าใจว่าค่าเริ่มต้นคือ val ในสกาล่า นี้:

def age = 30
age = 45

... แสดงข้อผิดพลาดในการรวบรวมเนื่องจากเป็นค่าเริ่มต้น เหตุใดชุดแรกของบรรทัดด้านบนจึงทำงานไม่ถูกต้องและยังไม่ผิดพลาด

คำตอบ:


254

มีสามวิธีในการกำหนดสิ่งต่าง ๆ ใน Scala:

  • defกำหนดวิธีการ
  • valกำหนดค่าคงที่(ซึ่งไม่สามารถแก้ไขได้)
  • varกำหนดตัวแปร (ซึ่งสามารถแก้ไขได้)

ดูรหัสของคุณ:

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

personนี้กำหนดวิธีการใหม่ที่เรียกว่า คุณสามารถเรียกใช้เมธอดนี้ได้โดยไม่ต้อง()เนื่องจากถูกกำหนดเป็นวิธีแบบไม่มีพารามิเตอร์ สำหรับวิธีการ paren ที่ว่างเปล่าคุณสามารถเรียกมันด้วยหรือไม่ใช้ '()' หากคุณเพียงแค่เขียน:

person

จากนั้นคุณกำลังเรียกวิธีนี้ (และถ้าคุณไม่ได้กำหนดค่าส่งคืนก็จะถูกทิ้ง) ในรหัสบรรทัดนี้:

person.age = 20

สิ่งที่เกิดขึ้นคือคุณเรียกpersonเมธอดก่อนและในค่าส่งคืน (อินสแตนซ์ของคลาสPerson) คุณกำลังเปลี่ยนageตัวแปรสมาชิก

และบรรทัดสุดท้าย:

println(person.age)

ที่นี่คุณจะเรียกใช้personเมธอดอีกครั้งซึ่งจะส่งคืนอินสแตนซ์ใหม่ของคลาสPerson(พร้อมageชุดเป็น 12) มันเหมือนกับ:

println(person().age)

27
เพื่อให้เกิดความสับสนสิ่งต่าง ๆ สถานะภายในของ a valสามารถเปลี่ยนแปลงได้ แต่วัตถุที่ถูกอ้างถึงโดย val ไม่สามารถทำได้ A valไม่ใช่ค่าคงที่
pferrel

5
เพื่อสร้างความสับสนให้กับสิ่งต่าง ๆ มากขึ้นวาล (และอาจจะหลากหลายเช่นกันฉันไม่ได้ลอง) สามารถใช้เพื่อกำหนดฟังก์ชัน เมื่อใช้ def เพื่อกำหนดฟังก์ชั่น / วิธีเนื้อความของ def จะถูกประเมินทุกครั้งที่มันถูกเรียก เมื่อใช้ val จะมีการประเมินที่จุดนิยามเท่านั้น ดูstackoverflow.com/questions/18887264/…
melston

1
@ เมลสตันใช่ แต่วิธีการและฟังก์ชั่นก็ไม่เหมือนกันทุกประการ
Jesper

3
เพื่อทำให้สับสนมากยิ่งขึ้น def สามารถใช้เพื่อกำหนดตัวแปรสมาชิกของคลาสได้โดยไม่จำเป็นต้องใช้ var
Peiti Li

2
@ pferrel ไม่ได้สับสนจริงๆ เหมือนกับสุดท้ายของ Java คุณสามารถทำเครื่องหมายListเป็นfinalแต่สามารถปรับเปลี่ยนเนื้อหาของมัน
jFrenetic

100

ผมขอเริ่มต้นด้วยความแตกต่างที่มีอยู่ในสกาลาระหว่างdef , Valและvar

  • def - กำหนดป้ายกำกับที่ไม่เปลี่ยนรูปสำหรับเนื้อหาด้านขวาซึ่งประเมินอย่างเกียจคร้าน - ประเมินโดยใช้ชื่อ

  • val - กำหนดป้ายกำกับที่ไม่เปลี่ยนรูปสำหรับเนื้อหาด้านขวาซึ่งถูกประเมินอย่างกระตือรือร้น / ทันที - ประเมินโดยมูลค่า

  • var - กำหนดตัวแปรที่ไม่แน่นอนซึ่งเริ่มแรกถูกตั้งค่าเป็นเนื้อหาด้านขวาที่ประเมิน

ตัวอย่าง def

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

ตัวอย่างวาล

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

ตัวอย่างเช่น

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

scala> aVariable = 5
aVariable: Int = 5

ตามที่กล่าวไว้ข้างต้นฉลากจากdefและvalไม่สามารถกำหนดใหม่ได้และในกรณีที่มีความพยายามใด ๆ จะเกิดข้อผิดพลาดเช่นด้านล่าง

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

ฉลากไม่เปลี่ยนรูปถูกสร้างขึ้นสำหรับกรณีเฉพาะของท่าน (เช่น 'Persona) เมื่อใดก็ตามที่ฟิลด์ 'อายุ' ไม่สามารถเปลี่ยนแปลงได้จำเป็นต้องแก้ไขความพยายามดังกล่าวจะล้มเหลว:

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') เป็นไปได้ที่จะแก้ไขฟิลด์ 'อายุ' คลาสที่ไม่แน่นอน

ฉันจะยังคงเน้นความจริงที่ว่าทุกอย่างมาจากความแตกต่างที่ระบุไว้ข้างต้นซึ่งจะต้องมีความชัดเจนในใจของโปรแกรมเมอร์สกาล่าใด ๆ


ฉันไม่คิดว่าคำอธิบายข้างต้นถูกต้อง ดูคำตอบอื่น ๆ
ต่อ Mildner

@PerMildner คุณช่วยอธิบายเกี่ยวกับสิ่งที่ผิดในคำตอบข้างต้นได้ไหม?
Syed Souban

ฉันจำไม่ได้ว่าคำร้องเรียนดั้งเดิมของฉันคืออะไร อย่างไรก็ตามส่วนสุดท้ายของคำตอบเกี่ยวกับpersonAet al ดูเหมือนจะปิด ไม่ว่าจะเป็นการปรับเปลี่ยนageการทำงานของสมาชิกหรือไม่เป็นอิสระจากว่าคุณจะใช้หรือdef personA var personBความแตกต่างคือในdef personAกรณีที่คุณกำลังปรับเปลี่ยนPerson-instance ที่ส่งคืนจากการประเมินครั้งแรกของpersonAคุณ กรณีนี้จะมีการปรับเปลี่ยน personAแต่มันก็ไม่ใช่สิ่งที่จะถูกส่งกลับเมื่อคุณประเมินอีกครั้ง แต่ครั้งที่สองที่คุณทำคุณจะมีประสิทธิภาพการทำpersonA.age new Person("Tim",25).age
ต่อ Mildner

29

กับ

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

คุณกำลังกำหนดตัวแปร function / lazy ซึ่งจะส่งคืนอินสแตนซ์ Person ใหม่ที่มีชื่อ "Kumar" และอายุ 12 อยู่เสมอซึ่งเป็นสิ่งที่ถูกต้องทั้งหมดและคอมไพเลอร์ไม่มีเหตุผลที่จะบ่น การเรียก person.age จะส่งคืนอายุของอินสแตนซ์ Person ที่สร้างขึ้นใหม่ซึ่งมีอายุ 12 ปีเสมอ

เมื่อเขียน

person.age = 45

varคุณกำหนดค่าใหม่ให้กับคุณสมบัติอายุในระดับบุคคลซึ่งเป็นที่ถูกต้องตั้งแต่อายุจะประกาศเป็น คอมไพเลอร์จะบ่นถ้าคุณพยายามที่จะมอบหมายให้personกับวัตถุคนใหม่เช่น

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

ใช่. จุดนี้สามารถแสดงให้เห็นได้อย่างง่ายดายโดยการเรียกใช้เมธอด hashCode กับบุคคลA
Nilanjan Sarkar

26

เพื่อให้มุมมองอื่น "def" ใน Scala หมายถึงสิ่งที่จะได้รับการประเมินในแต่ละครั้งเมื่อมีการใช้งานในขณะที่ Val เป็นสิ่งที่ประเมินได้ทันทีและเพียงครั้งเดียว ที่นี่การแสดงออกที่def person = new Person("Kumar",12)สร้างความหมายว่าเมื่อใดก็ตามที่เราใช้ "คน" เราจะได้รับnew Person("Kumar",12)สาย ดังนั้นจึงเป็นธรรมดาที่ "person.age" สองคนนั้นไม่เกี่ยวข้องกัน

นี่คือวิธีที่ฉันเข้าใจสกาล่า (อาจเป็นในลักษณะที่ "ใช้งานได้มากกว่า") ฉันไม่แน่ใจว่า

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

จริงๆแล้วเป็นสิ่งที่สกาล่าตั้งใจที่จะหมายถึง ฉันไม่ชอบคิดแบบนั้นอย่างน้อย ...


20

ตามที่คินทาโร่พูดไปแล้ว 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ในรหัสของคุณจะได้รับอนุญาตที่คุณได้รับกลับมาPersonเช่นจากวิธีการและในกรณีนี้คุณได้รับอนุญาตให้เปลี่ยนค่าของที่person varปัญหาคือว่าหลังจากบรรทัดนั้นคุณไม่มีการอ้างอิงถึงอินสแตนซ์นั้นอีกต่อไปpersonจะสร้างอินสแตนซ์ใหม่)

นี่ไม่มีอะไรพิเศษคุณจะมีพฤติกรรมเหมือนกันใน 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เป็นวิธีการ มันจะดำเนินการในแต่ละครั้งจะเรียกว่าและในแต่ละครั้งก็จะกลับ new Person("Kumar", 12)(ก) และสิ่งเหล่านี้ไม่ใช่ข้อผิดพลาดใน "การมอบหมาย" เพราะไม่ใช่การมอบหมาย แต่เป็นการโทรไปยังage_=วิธีการ (ให้บริการโดยvar)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.