เหตุใด "private val" และ "private final val" จึงแตกต่างกัน


101

ฉันเคยคิดแบบนั้นprivate valและprivate final valเหมือนกันจนกระทั่งฉันเห็นหัวข้อ 4.1 ใน Scala Reference:

นิยามค่าคงที่เป็นรูปแบบ

final val x = e

โดยที่ e คือนิพจน์คงที่ (§6.24) ต้องมีตัวปรับแต่งขั้นสุดท้ายและไม่สามารถระบุคำอธิบายประกอบประเภทได้ การอ้างอิงถึงค่าคงที่ x ถือว่าเป็นนิพจน์คงที่ ในโค้ดที่สร้างขึ้นจะถูกแทนที่ด้วย e ด้านขวามือของนิยาม

และฉันได้เขียนแบบทดสอบ:

class PrivateVal {
  private val privateVal = 0
  def testPrivateVal = privateVal
  private final val privateFinalVal = 1
  def testPrivateFinalVal = privateFinalVal
}

javap -c เอาต์พุต:

Compiled from "PrivateVal.scala"
public class PrivateVal {
  public int testPrivateVal();
    Code:
       0: aload_0       
       1: invokespecial #19                 // Method privateVal:()I
       4: ireturn       

  public int testPrivateFinalVal();
    Code:
       0: iconst_1      
       1: ireturn       

  public PrivateVal();
    Code:
       0: aload_0       
       1: invokespecial #24                 // Method java/lang/Object."<init>":()V
       4: aload_0       
       5: iconst_0      
       6: putfield      #14                 // Field privateVal:I
       9: return
}

รหัสไบต์เป็นเพียง Scala อ้างอิงกล่าวว่า: ไม่ได้private valprivate final val

ทำไมscalacถึงไม่ปฏิบัติprivate valตามprivate final val? มีเหตุผลพื้นฐานหรือไม่?


29
กล่าวอีกนัยหนึ่ง: เนื่องจาก a valไม่เปลี่ยนรูปอยู่แล้วทำไมเราถึงต้องการfinalคำหลักใน Scala เลย? เหตุใดคอมไพเลอร์จึงไม่สามารถปฏิบัติต่อทั้งหมดvalได้เช่นเดียวกับfinal vals
Jesper

โปรดสังเกตว่าprivateตัวปรับขอบเขตมีความหมายเช่นเดียวกับpackage privateใน Java private[this]คุณอาจจะหมายถึงว่า
Connor Doyle

5
@ConnorDoyle: ในฐานะแพคเกจส่วนตัว? ผมไม่คิดอย่างนั้น: privateหมายถึงว่ามันเป็นเพียงมองเห็นกรณีของชั้นนี้private[this]เพียง แต่กรณีนี้ - ยกเว้นกรณีของการเดียวกันระดับ , privateไม่อนุญาตให้ทุกคน (รวมถึงจากแพ็กเกจเดิม) ในการเข้าถึงค่า
ทำให้ 42

คำตอบ:


83

ดังนั้นนี่เป็นเพียงการคาดเดา แต่มันเป็นความน่ารำคาญตลอดกาลใน Java ที่ตัวแปรคงที่สุดท้ายที่มีลิเทอรัลทางด้านขวามือจะถูกแทรกใน bytecode เป็นค่าคงที่ สิ่งนี้ก่อให้เกิดประโยชน์ด้านประสิทธิภาพอย่างแน่นอน แต่จะทำให้ความเข้ากันได้แบบไบนารีของนิยามหยุดชะงักหาก "ค่าคงที่" เปลี่ยนแปลงไป เมื่อกำหนดตัวแปรคงที่สุดท้ายซึ่งค่าอาจจำเป็นต้องเปลี่ยนแปลงโปรแกรมเมอร์ Java ต้องใช้แฮ็กเช่นการกำหนดค่าเริ่มต้นด้วยวิธีการหรือตัวสร้าง

Val ใน Scala เป็นที่สิ้นสุดแล้วในความหมายของ Java ดูเหมือนว่านักออกแบบของ Scala จะใช้ตัวปรับแต่งซ้ำซ้อนในขั้นสุดท้ายเพื่อหมายถึง "การอนุญาตให้แทรกค่าคงที่" ดังนั้นโปรแกรมเมอร์ของ Scala จึงสามารถควบคุมพฤติกรรมนี้ได้อย่างสมบูรณ์โดยไม่ต้องใช้แฮ็ก: หากพวกเขาต้องการค่าคงที่แบบอินไลน์ค่าที่ไม่ควรเปลี่ยนแปลง แต่รวดเร็วพวกเขาจะเขียน "ค่าสุดท้าย" หากพวกเขาต้องการความยืดหยุ่นในการเปลี่ยนค่าโดยไม่ทำลายความเข้ากันได้ของไบนารีเพียงแค่ "val"


9
ใช่นั่นเป็นเหตุผลสำหรับ vals ที่ไม่ใช่แบบส่วนตัว แต่เห็นได้ชัดว่า vals ส่วนตัวไม่สามารถอินไลน์ในคลาสอื่น ๆ และทำลายความเข้ากันได้ในลักษณะเดียวกัน
Alexey Romanov

3
มีปัญหาความเข้ากันได้ของไบนารีเมื่อฉันเปลี่ยนprivate valเป็นprivate final valหรือไม่?
ยางบ่อ

1
@ steve-waldman ขอโทษนะคุณหมายถึงvalย่อหน้าที่สองหรือเปล่า
ยางบ่อ

1
นี่คือรายละเอียดเกี่ยวกับตัวแปรคงที่สุดท้ายใน Java เกี่ยวกับความเข้ากันได้ของไบนารี - docs.oracle.com/javase/specs/jls/se7/html/…
Eran Medan

8

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

@Brian REPL ให้ขอบเขตคลาสที่ระดับบรรทัด ดู:

scala> $iw.getClass.getPackage
res0: Package = package $line3

scala> private val x = 5
<console>:5: error: value x cannot be accessed in object $iw
  lazy val $result = `x`

scala> private val x = 5; println(x);
5

1
ฉันกำลังพูดถึงprivate val. สามารถลบล้างได้หรือไม่?
ยางบ่อ

ไม่ได้ไม่สามารถลบล้าง vals ส่วนตัวได้ คุณสามารถกำหนดวาลส่วนตัวอื่นที่มีชื่อเดียวกันในคลาสย่อยได้ แต่เป็นวาลที่แตกต่างกันโดยสิ้นเชิงที่มีชื่อเดียวกัน (การอ้างอิงถึงอันเก่าทั้งหมดจะยังคงอ้างถึงอันเก่า)
aij

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