riêng tư [cái này] so với riêng tư


111

Trong Scala, tôi thấy tính năng như biến đối tượng-riêng. Từ nền tảng Java không phong phú của mình, tôi đã học cách đóng mọi thứ (đặt nó ở chế độ riêng tư) và mở (cung cấp trình truy cập) nếu cần. Scala giới thiệu công cụ sửa đổi truy cập thậm chí nghiêm ngặt hơn. Tôi có nên luôn sử dụng nó theo mặc định không? Hay tôi chỉ nên sử dụng nó trong một số trường hợp cụ thể mà tôi cần hạn chế rõ ràng việc thay đổi giá trị trường ngay cả đối với các đối tượng của cùng một lớp? Nói cách khác, tôi nên chọn như thế nào giữa

class Dummy {
    private var name = "default name"
}

class Dummy {
    private[this] var name = "default name"
}

Thứ hai là nghiêm ngặt hơn và tôi thích nó nhưng tôi có nên sử dụng nó luôn không hay chỉ khi tôi có lý do chính đáng?

CHỈNH SỬA: Như tôi thấy ở đây private[this] chỉ là một số chữ hoa con và thay vì thistôi có thể sử dụng các bổ ngữ khác: "đối tượng package, class hoặc singleton". Vì vậy, tôi sẽ để nó cho một số trường hợp đặc biệt.


Câu trả lời:


59

Tôi không nghĩ nó quá quan trọng, vì bất kỳ thay đổi nào cũng sẽ chỉ chạm đến một lớp. Vì vậy, lý do quan trọng nhất để thích privatehơn protectedso với publickhông áp dụng.

Sử dụng private[this]nơi hiệu suất thực sự quan trọng (vì bạn sẽ có quyền truy cập trường trực tiếp thay vì các phương pháp theo cách này). Nếu không, chỉ cần giải quyết trên một phong cách để mọi người không cần phải tìm ra lý do tại sao này sở hữu là privateđó là một trong private[this].


6
@ om-nom-nom Thật ra, không có nhiều điều để kể. Dù sao thì JIT cũng nên nội tuyến các cuộc gọi phương thức trình truy cập được tạo ra private, do đó tác động phải bằng 0 hoặc ít nhất là rất nhỏ.
Alexey Romanov

9
Câu trả lời này gây hiểu lầm, lý do thực sự là phương sai của trang web khai báo (xem câu trả lời này: stackoverflow.com/a/9727849/445715 ).
Andrey Breslav

1
@AndreyBreslav Tôi không đồng ý rằng đây là các lý do. Có, trường hợp như vậy tồn tại nhưng như câu trả lời cho biết nó khá hiếm.
Alexey Romanov

3
Hừ! Câu trả lời của Marek Adamek dưới đây dường như là lý do thực sự để chọn private [điều này] thay vì private. Mục đích là giới hạn quyền truy cập vào một cá thể cụ thể, trái ngược với tất cả các phiên bản của lớp.
Ram Rajamony

3
@AlexeyRomanov - câu hỏi đặt ra "Tôi có nên luôn sử dụng nó theo mặc định không?". Tôi nghĩ bạn có thể cải thiện câu trả lời của mình bằng cách nói rằng không thể sử dụng private [this] nếu bạn cần trường từ một phiên bản khác của cùng một lớp.
Ram Rajamony

130

Có một trường hợp private[this]bắt buộc phải biên dịch mã. Điều này liên quan đến sự tương tác của ký hiệu phương sai và các biến có thể thay đổi. Hãy xem xét lớp (vô dụng) sau:

class Holder[+T] (initialValue: Option[T]) {
    // without [this] it will not compile
    private[this] var value = initialValue

    def getValue = value
    def makeEmpty { value = None }
}

Vì vậy, lớp này được thiết kế để giữ một giá trị tùy chọn, trả về nó dưới dạng tùy chọn và cho phép người dùng gọi makeEmptyđể xóa giá trị (do đó là var). Như đã nói, điều này là vô ích ngoại trừ việc chứng minh quan điểm.

Nếu bạn cố gắng biên dịch mã này với privatethay vì private[this]nó sẽ không thành công với thông báo lỗi sau:

error: loại hiệp phương sai T xảy ra ở vị trí đối nghịch trong loại Option [T] of value value_ = class Holder [+ T] (initialValue: Option [T]) {

Lỗi này xảy ra vì giá trị là một biến có thể thay đổi trên kiểu hiệp phương sai T (+ T) thường là một vấn đề trừ khi được đánh dấu là riêng tư đối với cá thể với private[this]. Trình biên dịch có cách xử lý đặc biệt trong việc kiểm tra phương sai của nó để xử lý trường hợp đặc biệt này.

Vì vậy, nó bí truyền nhưng có một trường hợp private[this]bắt buộc phải qua private.


1
Tôi có thể hiểu tại sao nó không thành công khi có khả năng thay đổi trong hỗn hợp, nhưng tại sao tôi lại gặp lỗi tương tự khi không có gì có thể thay đổi ?
Matt Kantor

34

private var namecó thể truy cập từ bất kỳ phương thức nào của class Dummy(và đồng hành của nó object Dummy).

private[this] var namechỉ có thể truy cập từ các phương thức của thisđối tượng, không phải từ các đối tượng khác của class Dummy.


18

private [this] (tương đương với protected [this]) có nghĩa là "y" chỉ hiển thị với các phương thức trong cùng một trường hợp. Ví dụ: bạn không thể tham chiếu y trên trường hợp thứ hai trong phương thức bằng, tức là "this.y == that.y" sẽ tạo ra lỗi biên dịch trên "that.y". (nguồn)

vì vậy bạn có thể thực hiện [điều này] riêng tư bất cứ lúc nào bạn muốn nhưng bạn có thể gặp một số vấn đề nếu bạn cần tham khảo


13
private[this]không bằng protected[this]. protected[this]cho phép các cá thể của lớp con truy cập thành viên.
drexin

Bạn có thể làm this.y == that.ybằng không tin cũng không tin [này], tôi chỉ cố gắng cả
lisak

12

Điều này đã được kiểm tra bằng cách sử dụng scala 2.11.5. Hãy xem xét đoạn mã dưới đây

class C(private val x: Int) {
  override def equals(obj: Any) = obj match {
    case other: C => x == other.x
    case _ => false
  }
}

println(new C(5) == new C(5)) //true
println(new C(5) == new C(4)) //false

nó sẽ biên dịch và hoạt động như mã java (1.8) này

class C {
    private int x;

    public C(int x) {
        this.x = x;
    }

    public boolean equals(Object obj) {
        if (obj instanceof C) {
            return ((C) obj).x == x;
        }
        else {
            return false;
        }
    }
}

System.out.println(new C(5).equals(new C(5))); //true
System.out.println(new C(5).equals(new C(4))); //false

tuy nhiên nếu bạn sử dụng công cụ sửa đổi '[this]', mã bên dưới sẽ không biên dịch

class C(private[this] val x: Int) {
  override def equals(obj: Any) = obj match {
    case other: C => this.x == other.x //problem is here
    case _ => false
  }
}

Điều này là do trong trường hợp đầu tiên, 'x' có thể truy cập được ở cấp độ lớp, trong khi ở trường hợp thứ hai là cấp độ bản sao nghiêm ngặt hơn. Nó có nghĩa là 'x' chỉ có thể được truy cập từ phiên bản mà nó thuộc về. Vì vậy, 'this.x' là tốt nhưng 'other.x' thì không.

Bạn có thể tham khảo phần 13.5 của cuốn sách "Lập trình trong Scala: Hướng dẫn Toàn diện Từng bước" để biết thêm chi tiết về các công cụ sửa đổi truy cập.


1
Câu hỏi không hỏi private[this]nghĩa là gì . Lưu ý câu đầu tiên.
Alexey Romanov

9

Khi thêm phạm vi vào công cụ sửa đổi private ( private [X] ), nó hoạt động hiệu quả như một "tối đa" X, trong đó X chỉ định một số gói, lớp hoặc đối tượng singleton bao quanh.

Ví dụ: private [bar] , trong đó thanh là một gói có nghĩa là mọi phiên bản của mọi lớp thuộc thanh gói đều có thể truy cập bất kỳ thành viên nào mà công cụ sửa đổi đang hạn chế.

Trong trường hợp riêng tư [này] , điều đó có nghĩa là thành viên chỉ có thể truy cập cho mỗi trường hợp. Điều này trở nên rõ ràng hơn trong ví dụ sau:

class Foo(foo:Foo){  
   private[this] val i = 2
   println(this.i + foo.i)
}

>>error: value i is not a member of Foo

class Foo(foo:Foo){  
    private val i = 2
    println(this.i + foo.i)
}

>>defined class Foo

Như bạn có thể thấy, Foo thứ hai không có bất kỳ vấn đề gì vì bất kỳ phiên bản nào cũng có thể truy cập val i riêng. Tuy nhiên, đối với Foo đầu tiên, có một lỗi vì mỗi phiên bản không thể thấy i của phiên bản khác.

Bạn nên viết riêng tư [điều này] vì nó áp đặt một hạn chế lớn hơn.


6

Trong hầu hết các ngôn ngữ lập trình OOP như java, các trường / phương thức riêng có nghĩa là các trường / phương thức riêng này không thể truy cập bên ngoài từ lớp. Tuy nhiên, các cá thể / đối tượng của cùng một lớp có thể có quyền truy cập vào các trường riêng của các đối tượng bằng cách sử dụng toán tử gán hoặc bằng phương thức khởi tạo sao chép. Trong Scala, private [this] là đối tượng private, điều này đảm bảo rằng bất kỳ đối tượng nào khác của cùng một lớp đều không thể truy cập các thành viên private [this].

Thí dụ

1. Không riêng tư [cái này]

object ObjectPrivateDemo {

  def main(args: Array[String]) {
    var real = new User("realUserName", "realPassword")
    var guest = new User("dummyUserName", "dummyPassword")
    real.displayUser(guest)

  }
}

class User(val username:String,val password:String) {
  private var _username=username
  private var _password=password



  def displayUser(guest:User){

         println(" guest username="+guest._username+" guest password="+guest._password)
       guest._username= this._username
    guest._password=  this._password
       println(" guest username="+guest._username+" guest password="+guest._password)


  }
}

2. sử dụng riêng tư [cái này]

class User(val username: String, val password: String) {
  private var _username = username
  private[this] var _password = password



  def displayUser(guest: User) {

    println(this._username)
    println(this._password)

    guest._username = this._username
    // for guest._password it will give this :error  value _password is not member of class User
    guest._password = this._password

  }
}

Do đó private [this] đảm bảo rằng trường _password chỉ có thể truy cập được với cái này.


Đây là câu trả lời rõ ràng và khách quan hơn.
Lucas Lima

2

Để giải thích rõ hơn về vấn đề hiệu suất mà Alexey Romanov đã đề cập, đây là một số phỏng đoán của tôi. Trích dẫn từ cuốn sách "Lập trình trong Scala: Hướng dẫn Toàn diện Từng bước, Tái bản lần 2" Phần 18.2:

Trong Scala, mọi var không phải là thành viên private của một số đối tượng sẽ định nghĩa ngầm một phương thức getter và setter với nó.

Để kiểm tra, mã này sẽ gây ra lỗi biên dịch:

class PrivateTest{
  var data: Int = 0
  def data_=(x : Int){
    require(x > 0)
    data = x
  }
}

Scala phàn nàn về error: ambiguous reference to overloaded definition. Việc thêm từ khóa ghi đè vào data_=sẽ không giúp chứng minh rằng phương thức được tạo bởi trình biên dịch. Việc thêm privatetừ khóa vào biến datasẽ vẫn gây ra lỗi biên dịch này. Tuy nhiên, mã sau đây biên dịch tốt:

class PrivateTest{
  private[this] var data: Int = 0
  def data_=(x : Int){
    require(x > 0)
    data = x
  }
}

Vì vậy, tôi đoán private[this]sẽ ngăn scala tạo ra các phương thức getter và setter. Do đó, việc truy cập biến như vậy sẽ tiết kiệm chi phí gọi phương thức getter và setter.


1

Tôi có nên luôn sử dụng nó theo mặc định không? Hay tôi chỉ nên sử dụng nó trong một số trường hợp cụ thể mà tôi cần hạn chế rõ ràng việc thay đổi giá trị trường ngay cả đối với các đối tượng của cùng một lớp? Nói cách khác, tôi nên chọn như thế nào giữa

Tốt hơn là nên sử dụng private[this]nếu bạn định đồng bộ hóa biến.

Dưới đây là một ví dụ điển hình từ hướng dẫn kiểu scala của nhóm Spark :

// The following is still unsafe.
class Foo {
  private var count: Int = 0
  def inc(): Unit = synchronized { count += 1 }
}

// The following is safe.
class Foo {
  private[this] var count: Int = 0
  def inc(): Unit = synchronized { count += 1 }
}
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.