ส่วนตัว [นี้] กับส่วนตัว


112

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

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

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

อย่างที่สองเข้มงวดกว่าและฉันชอบ แต่ฉันควรใช้มันเสมอหรือถ้าฉันมีเหตุผลที่ชัดเจน

แก้ไข: อย่างที่ฉันเห็นนี่ private[this]เป็นเพียงตัวพิมพ์เล็กบางส่วนและthisฉันสามารถใช้ตัวดัดแปลงอื่น ๆ แทน: "package, class หรือ singleton object" ได้ ผมจะปล่อยไว้เป็นกรณีพิเศษ


คำตอบ:


59

ฉันไม่คิดว่ามันสำคัญมากนักเนื่องจากการเปลี่ยนแปลงใด ๆ จะกระทบเพียงคลาสเดียวไม่ว่าจะด้วยวิธีใดก็ตาม ดังนั้นเหตุผลที่สำคัญที่สุดที่จะชอบprivateมากกว่าprotectedมากกว่าpublicไม่ได้ใช้

ใช้ในprivate[this]ที่ที่ประสิทธิภาพมีความสำคัญมาก (เนื่องจากคุณจะได้รับการเข้าถึงโดยตรงจากสนามแทนที่จะใช้วิธีนี้) มิฉะนั้นเพียงแค่ชำระในรูปแบบหนึ่งเพื่อให้คนไม่จำเป็นต้องคิดออกว่าทำไมนี้ทรัพย์สินprivateและที่private[this]หนึ่งคือ


6
@ om-nom-nom จริงๆแล้วไม่มีอะไรจะบอกมาก JIT ควรอินไลน์การเรียกเมธอด accessor ที่สร้างขึ้นprivateดังนั้นผลกระทบควรเป็นศูนย์หรืออย่างน้อยก็น้อยมาก
Alexey Romanov

9
คำตอบนี้ทำให้เข้าใจผิดสาเหตุที่แท้จริงคือความแปรปรวนของการประกาศ - ไซต์ (ดูคำตอบนี้: stackoverflow.com/a/9727849/445715 )
Andrey Breslav

1
@AndreyBreslav ผมไม่เห็นว่านี่คือเหตุผล ใช่กรณีดังกล่าวมีอยู่จริง แต่ตามที่คำตอบบอกว่าค่อนข้างหายาก
Alexey Romanov

3
อืม. คำตอบของ Marek Adamek ด้านล่างดูเหมือนจะเป็นเหตุผลที่แท้จริงในการเลือก private [this] มากกว่า private จุดประสงค์คือเพื่อ จำกัด การเข้าถึงอินสแตนซ์เฉพาะซึ่งตรงข้ามกับอินสแตนซ์ทั้งหมดของคลาส
รามราชมงคล

3
@AlexeyRomanov - คำถามที่ถามว่า "ควรใช้เป็นค่าเริ่มต้นเสมอหรือไม่" ฉันคิดว่าคุณสามารถปรับปรุงคำตอบของคุณได้โดยบอกว่าไม่สามารถใช้ [this] ส่วนตัวได้หากคุณต้องการฟิลด์จากอินสแตนซ์อื่นของคลาสเดียวกัน
รามราชมงคล

130

มีกรณีที่private[this]จำเป็นในการคอมไพล์โค้ด สิ่งนี้เกี่ยวข้องกับปฏิสัมพันธ์ของสัญกรณ์ความแปรปรวนและตัวแปรที่เปลี่ยนแปลงได้ พิจารณาคลาส (ไร้ประโยชน์) ต่อไปนี้:

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

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

ดังนั้นคลาสนี้ได้รับการออกแบบมาเพื่อเก็บค่าที่เป็นทางเลือกส่งคืนเป็นตัวเลือกและอนุญาตให้ผู้ใช้เรียกmakeEmptyเพื่อล้างค่า (ดังนั้นจึงเป็นตัวแปร) ตามที่ระบุไว้สิ่งนี้ไม่มีประโยชน์ยกเว้นการแสดงให้เห็นถึงประเด็น

หากคุณพยายามรวบรวมรหัสนี้privateแทนprivate[this]จะล้มเหลวด้วยข้อความแสดงข้อผิดพลาดต่อไปนี้:

error: covariant type T เกิดขึ้นใน contravariant position ใน type Option [T] ของ value value_ = class Holder [+ T] (initialValue: Option [T]) {

ข้อผิดพลาดนี้เกิดขึ้นเนื่องจากค่าคือตัวแปรที่ไม่แน่นอนกับชนิด covariant T (+ T) private[this]ซึ่งเป็นปกติปัญหาเว้นแต่ทำเครื่องหมายเป็นส่วนตัวอินสแตนซ์ที่มี คอมไพเลอร์มีการจัดการพิเศษในการตรวจสอบความแปรปรวนเพื่อจัดการกับกรณีพิเศษนี้

ดังนั้นจึงเป็นความลับ แต่มีกรณีที่จะต้องมากกว่าprivate[this]private


1
ฉันสามารถดูว่าทำไมมันล้มเหลวเมื่อมีความไม่แน่นอนอยู่ในการผสม แต่ทำไมผมได้รับข้อผิดพลาดเดียวกันเมื่อไม่มีอะไรแน่นอน ?
Matt Kantor

35

private var nameสามารถเข้าถึงได้จากวิธีการใด ๆ ของclass Dummy(และเพื่อนร่วมทางobject Dummy)

private[this] var nameสามารถเข้าถึงได้จากวิธีการของthisวัตถุเพียงอย่างเดียวไม่ได้มาจากวัตถุอื่น ๆ class Dummyของ


18

ส่วนตัว [this] (เทียบเท่ากับการป้องกัน [นี้]) หมายความว่า "y" จะมองเห็นได้เฉพาะเมธอดในอินสแตนซ์เดียวกัน ตัวอย่างเช่นคุณไม่สามารถอ้างอิง y บนอินสแตนซ์ที่สองในวิธีการเท่ากับกล่าวคือ "this.y == that.y" จะสร้างข้อผิดพลาดในการคอมไพล์บน "that.y" (ที่มา)

เพื่อให้คุณสามารถทำ [สิ่งนี้] แบบส่วนตัวได้ทุกครั้งที่คุณต้องการ แต่คุณอาจมีปัญหาได้หากต้องการอ้างอิง


13
private[this]ไม่เท่ากับprotected[this]. protected[this]อนุญาตให้อินสแตนซ์คลาสย่อยเข้าถึงสมาชิกได้
drexin

คุณไม่สามารถทำได้this.y == that.yโดยใช้ทั้งแบบส่วนตัวหรือแบบส่วนตัว [สิ่งนี้] ฉันเพิ่งลองทั้งสองอย่าง
lisak

12

ทดสอบโดยใช้สกาล่า 2.11.5 พิจารณาโค้ดด้านล่าง

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

มันจะคอมไพล์และทำงานเป็นโค้ด java (1.8) นี้

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

อย่างไรก็ตามหากคุณใช้ตัวแก้ไข '[this]' โค้ดด้านล่างจะไม่รวบรวม

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
  }
}

เนื่องจากในกรณีแรก "x" สามารถเข้าถึงได้ในระดับชั้นเรียนในขณะที่ในกรณีที่สองจะเป็นระดับอินสแตนซ์ที่เข้มงวดกว่า หมายความว่า 'x' สามารถเข้าถึงได้จากอินสแตนซ์ที่เป็นสมาชิกเท่านั้น ดังนั้น 'this.x' ก็ใช้ได้ แต่ 'other.x' ไม่ใช่

คุณสามารถดูส่วนที่ 13.5 ของหนังสือ "Programming in Scala: A Comprehensive Step-By-Step Guide" สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับตัวปรับแต่งการเข้าถึง


1
คำถามไม่ได้ถามว่าprivate[this]หมายถึงอะไร สังเกตประโยคแรก
Alexey Romanov

9

เมื่อเพิ่มขอบเขตให้กับโมดิฟายเออร์ส่วนตัว ( ไพรเวต [X] ) มันจะทำงานเป็นXได้อย่างมีประสิทธิภาพโดยที่ X กำหนดแพ็กเกจที่ปิดล้อมคลาสหรืออ็อบเจ็กต์เดี่ยว

ตัวอย่างเช่นprivate [bar]โดยที่barเป็นแพ็กเกจหมายความว่าทุกอินสแตนซ์ของทุกคลาสที่อยู่ในแถบแพ็กเกจสามารถเข้าถึงสมาชิกคนใดก็ตามที่โมดิฟายเออร์ จำกัด อยู่

ในกรณีส่วนตัว [นี้]หมายความว่าสมาชิกสามารถเข้าถึงได้สำหรับแต่ละอินสแตนซ์เท่านั้น สิ่งนี้ชัดเจนยิ่งขึ้นในตัวอย่างต่อไปนี้:

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

อย่างที่คุณเห็น Foo ตัวที่สองไม่มีปัญหาใด ๆ เนื่องจากอินสแตนซ์ใด ๆ สามารถเข้าถึงส่วนตัว val i ได้ อย่างไรก็ตามสำหรับ Foo แรกมีข้อผิดพลาดเนื่องจากแต่ละอินสแตนซ์ไม่เห็น i ของอินสแตนซ์อื่น

การเขียนส่วนตัว [this] เป็นวิธีปฏิบัติที่ดีเนื่องจากมีข้อ จำกัด ที่ใหญ่กว่า


6

ในภาษาการเขียนโปรแกรม OOP ส่วนใหญ่เช่น java ฟิลด์ / วิธีการส่วนตัวหมายความว่าฟิลด์ / เมธอดส่วนตัวเหล่านี้ไม่สามารถเข้าถึงได้จากภายนอกคลาส อย่างไรก็ตามอินสแตนซ์ / ออบเจ็กต์ของคลาสเดียวกันสามารถเข้าถึงฟิลด์ส่วนตัวของอ็อบเจ็กต์โดยใช้ตัวดำเนินการกำหนดหรือโดยใช้ตัวสร้างการคัดลอก ใน Scala ไพรเวต [this] เป็นอ็อบเจ็กต์ส่วนตัวซึ่งทำให้แน่ใจว่าอ็อบเจ็กต์อื่น ๆ ในคลาสเดียวกันไม่สามารถเข้าถึงสมาชิกส่วนตัว [นี้] ได้

ตัวอย่าง

1. ไม่มีความเป็นส่วนตัว [นี้]

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. การใช้ส่วนตัว [สิ่งนี้]

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

  }
}

ดังนั้นส่วนตัว [สิ่งนี้] ทำให้แน่ใจว่าฟิลด์ _password สามารถเข้าถึงได้ด้วยสิ่งนี้เท่านั้น


นี่เป็นคำตอบที่ชัดเจนและมีวัตถุประสงค์มากขึ้น
Lucas Lima

2

เพื่ออธิบายรายละเอียดเกี่ยวกับปัญหาด้านประสิทธิภาพที่ Alexey Romanov ได้กล่าวไว้นี่คือการคาดเดาบางส่วนของฉัน คำพูดจากหนังสือ "Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition" ตอนที่ 18.2:

ใน Scala ทุก var ที่ไม่ใช่สมาชิกส่วนตัวของอ็อบเจ็กต์บางอย่างจะกำหนด getter และเมธอด setter ด้วยโดยปริยาย

ในการทดสอบรหัสนี้จะทำให้เกิดข้อผิดพลาดในการคอมไพล์:

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

สกาล่าบ่นเรื่องerror: ambiguous reference to overloaded definition. การเพิ่มคีย์เวิร์ดแทนที่data_=ไม่ช่วยควรพิสูจน์ว่าเมธอดถูกสร้างขึ้นโดยคอมไพลเลอร์ การเพิ่มprivateคีย์เวิร์ดให้กับตัวแปรdataจะยังคงทำให้เกิดข้อผิดพลาดในการคอมไพล์ อย่างไรก็ตามรหัสต่อไปนี้รวบรวมได้ดี:

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

ดังนั้นฉันเดาว่าprivate[this]จะป้องกันไม่ให้ scala สร้างวิธี getter และ setter ดังนั้นการเข้าถึงตัวแปรดังกล่าวจะช่วยประหยัดค่าใช้จ่ายในการเรียกใช้ getter และ setter method


1

ฉันควรใช้เป็นค่าเริ่มต้นหรือไม่? หรือฉันควรใช้มันเฉพาะในบางกรณีที่ฉันจำเป็นต้อง จำกัด การเปลี่ยนแปลงค่าฟิลด์อย่างชัดเจนแม้กระทั่งสำหรับออบเจ็กต์ในคลาสเดียวกัน ฉันควรเลือกอย่างไรระหว่าง

จะดีกว่าprivate[this]ถ้าคุณวางแผนที่จะซิงโครไนซ์ตัวแปร

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