Tại sao `private val` và` private final val` khác nhau?


100

Tôi đã từng nghĩ điều đó private valprivate final valcũng giống như vậy, cho đến khi tôi xem phần 4.1 trong Scala Reference:

Định nghĩa giá trị không đổi có dạng

final val x = e

với e là một biểu thức hằng số (§6.24). Công cụ sửa đổi cuối cùng phải có mặt và không có chú thích loại nào có thể được cung cấp. Các tham chiếu đến giá trị hằng số x được coi là biểu thức hằng số; trong mã được tạo, chúng được thay thế bằng phía bên phải của định nghĩa e.

Và tôi đã viết một bài kiểm tra:

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

javap -c đầu ra:

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
}

Mã byte đúng như Scala Reference đã nói: private valkhông phải private final val.

Tại sao scalac không được coi private valprivate final val? Có lý do cơ bản nào không?


28
Nói cách khác: vì a valđã là bất biến, tại sao chúng ta lại cần finaltừ khóa trong Scala? Tại sao trình biên dịch không thể xử lý tất cả các vals theo cùng một cách với final vals?
Jesper

Lưu ý rằng công cụ privatesửa đổi phạm vi có ngữ nghĩa giống như package privatetrong Java. Bạn có thể muốn nói private[this].
Connor Doyle

5
@ConnorDoyle: Như gói riêng tư? Tôi không nghĩ vậy: privatecó nghĩa là nó chỉ hiển thị với các cá thể của lớp này, private[this]chỉ cá thể này - ngoại trừ các trường hợp của cùng một lớp , privatekhông cho phép bất kỳ ai (bao gồm từ cùng một gói) truy cập giá trị.
Make42,

Câu trả lời:


81

Vì vậy, đây chỉ là một phỏng đoán, nhưng nó là một khó chịu lâu năm trong Java rằng các biến tĩnh cuối cùng với một ký tự ở phía bên tay phải được đưa vào bytecode dưới dạng hằng số. Điều đó chắc chắn tạo ra một lợi ích về hiệu suất, nhưng nó khiến khả năng tương thích nhị phân của định nghĩa bị phá vỡ nếu "hằng số" từng thay đổi. Khi xác định một biến tĩnh cuối cùng mà giá trị của nó có thể cần thay đổi, các lập trình viên Java phải sử dụng đến các thủ thuật như khởi tạo giá trị bằng một phương thức hoặc hàm tạo.

Val trong Scala đã là cuối cùng theo nghĩa Java. Có vẻ như các nhà thiết kế của Scala đang sử dụng công cụ sửa đổi dư thừa cuối cùng có nghĩa là "quyền để nội dòng giá trị không đổi". Vì vậy, các lập trình viên Scala có toàn quyền kiểm soát hành vi này mà không cần dùng đến hack: nếu họ muốn một hằng số nội tuyến, một giá trị không bao giờ thay đổi nhưng nhanh, họ viết "giá trị cuối cùng". nếu họ muốn thay đổi giá trị một cách linh hoạt mà không phá vỡ khả năng tương thích nhị phân, chỉ cần "val".


9
Vâng, đó là lý do cho các vals không phải private, nhưng private vals rõ ràng không thể được đặt trong các lớp khác và phá vỡ khả năng tương thích theo cách tương tự.
Alexey Romanov,

3
Có bất kỳ vấn đề tương thích nhị phân nào khi tôi thay đổi private valthành private final valkhông?
Yang Bo

1
@ steve-waldman Xin lỗi, ý bạn là valtrong đoạn thứ hai?
Yang Bo

1
Dưới đây là chi tiết về các biến tĩnh cuối cùng trong Java liên quan đến khả năng tương thích nhị phân - docs.oracle.com/javase/specs/jls/se7/html/…
Eran Medan

8

Tôi nghĩ rằng sự nhầm lẫn ở đây phát sinh từ việc kết hợp tính bất biến với ngữ nghĩa của cuối cùng. vals có thể được ghi đè trong các lớp con và do đó không thể được coi là cuối cùng trừ khi được đánh dấu rõ ràng như vậy.

@Brian REPL cung cấp phạm vi lớp ở cấp độ dòng. Xem:

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
Tôi đang nói về private val. Nó có thể được ghi đè?
Yang Bo

Không, không thể ghi đè vals riêng tư. Bạn có thể xác định lại một val riêng khác có cùng tên trong một lớp con, nhưng đó là một val hoàn toàn khác mà chỉ xảy ra trùng tên. (Tất cả các tài liệu tham khảo để cái cũ vẫn sẽ đề cập đến một tuổi.)
aij

1
nó dường như không chỉ là hành vi ghi đè này, vì tôi có thể tạo giá trị cuối cùng (hoặc thậm chí là var cuối cùng) ở trình thông dịch mà không cần ở trong ngữ cảnh của một lớp nào cả.
nairbv
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.