Kotlin Backing Field สำหรับอะไร?


93

ในฐานะนักพัฒนา Java แนวคิดของเขตข้อมูลสำรองค่อนข้างแปลกสำหรับฉัน ให้:

class Sample {
    var counter = 0 // the initializer value is written directly to the backing field
    set(value) {
        if (value >= 0) field = value
    }
}

สนามสำรองนี้เหมาะสำหรับอะไร? Kotlin เอกสารกล่าวว่า:

ชั้นเรียนใน Kotlin ไม่สามารถมีเขตข้อมูลได้ แต่บางครั้งก็มีความจำเป็นต้องมีข้อมูลสนับสนุนเมื่อใช้ accessors

ทำไม? อะไรคือความแตกต่างกับการใช้ชื่อคุณสมบัติภายในตัวเซ็ตเตอร์เช่น *

class Sample {        
    var counter = 0
    set(value) {
        if (value >= 0) this.counter = value // or just counter = value?
    }
}

18
การใช้คุณสมบัติในตัวเซ็ตเตอร์จะทำให้เกิดการวนซ้ำไม่รู้จบเนื่องจากการกำหนดค่าบางอย่างให้กับคุณสมบัติจะเรียกตัวเซ็ตเสมอ
funglejunk

1
@Strelok ไม่ดีของฉัน .... ฉันคิดว่าthis.counter = valueมันเหมือนกันกับ Java ที่เทียบเท่าเมื่ออ่านเอกสารของ Kotlin
Yudhistira Arya

5
นี้บทความสำหรับฟิลด์ VS ทรัพย์สิน
avinash

คำตอบ:


86

เพราะบอกว่าถ้าคุณไม่ได้มีfieldคำหลักที่คุณจะไม่สามารถที่จะตั้งจริง / ได้รับค่าในหรือget() set(value)ช่วยให้คุณเข้าถึงฟิลด์สำรองในตัวเข้าถึงที่กำหนดเอง

นี่คือโค้ด Java ที่เทียบเท่ากับตัวอย่างของคุณ:

class Sample {
    private int counter = 0;
    public void setCounter(int value) {
        if (value >= 0) setCounter(value);
    }
    public int getCounter() {
        return counter;
    }
}

เห็นได้ชัดว่าสิ่งนี้ไม่ดีเนื่องจาก setter เป็นเพียงการเรียกซ้ำแบบ infinte ในตัวเองไม่เคยเปลี่ยนแปลงอะไรเลย จำไว้ว่าใน kotlin เมื่อใดก็ตามที่คุณเขียนfoo.bar = valueมันจะถูกแปลเป็นการเรียก setter แทนที่จะเป็นPUTFIELDไฟล์.


แก้ไข: Java มีฟิลด์ในขณะที่ Kotlin มีคุณสมบัติซึ่งเป็นแนวคิดระดับค่อนข้างสูงกว่าฟิลด์

คุณสมบัติมีสองประเภท: หนึ่งมีเขตข้อมูลสำรองและหนึ่งไม่มี

คุณสมบัติที่มีฟิลด์สำรองจะจัดเก็บค่าในรูปแบบของฟิลด์ ฟิลด์นั้นทำให้การจัดเก็บค่าในหน่วยความจำทำได้ ตัวอย่างของสถานที่ให้บริการดังกล่าวเป็นfirstและคุณสมบัติของsecond ว่าทรัพย์สินจะเปลี่ยนเป็นตัวแทนในหน่วยความจำของPairPair

คุณสมบัติที่ไม่มีฟิลด์สำรองจะต้องจัดเก็บค่าด้วยวิธีอื่นนอกเหนือจากการจัดเก็บไว้ในหน่วยความจำโดยตรง ต้องคำนวณจากคุณสมบัติอื่นหรือตัววัตถุเอง ตัวอย่างของคุณสมบัติดังกล่าวคือคุณสมบัติindicesส่วนขยายListซึ่งไม่ได้รับการสนับสนุนจากฟิลด์ แต่เป็นผลลัพธ์ที่คำนวณโดยยึดตามsizeคุณสมบัติ ดังนั้นจะไม่เปลี่ยนการแสดงในหน่วยความจำList(ซึ่งไม่สามารถทำได้เลยเพราะ Java พิมพ์แบบคงที่)


ขอบคุณสำหรับคำตอบ! ฉันไม่ดี ... ฉันคิดว่าthis.counter = valueมันเหมือนกันกับ java ที่เทียบเท่า
Yudhistira Arya

เอกสารทุกที่? ขอบคุณ :)
Alston

@Alston นี่คือเอกสาร: kotlinlang.org/docs/reference/properties.html#backing-fields
Yamashiro Rion

1
คำอธิบายที่คลุมเครือมากมาย ทำไมเราพูดชัด ๆ ไม่ได้ว่า a fieldเป็นเหมือนตัวชี้หรือการอ้างอิงตัวแปรสมาชิกที่มีอยู่ ตั้งแต่get/setทันที followes counterดังนั้นคำหลักคือการอ้างอิงถึงfield counterขวา?
eigenfield

@typelogic คำตอบนี้เหมาะที่สุดสำหรับโปรแกรมเมอร์ที่มีพื้นหลัง Java / JS (ในตอนนั้นไม่มี Kotlin / Native) ไม่ใช่ C / C ++ สิ่งที่คุณพบคือขนมปังและเนยสำหรับคนอื่น ๆ
glee8e

32

ในตอนแรกฉันก็มีช่วงเวลาที่ยากลำบากในการทำความเข้าใจแนวคิดนี้ ดังนั้นให้ฉันอธิบายให้คุณได้รับความช่วยเหลือจากตัวอย่าง

พิจารณาคลาส Kotlin นี้

class DummyClass {
    var size = 0;
    var isEmpty
        get() = size == 0
        set(value) {
            size = size * 2
        }
}

ตอนนี้เมื่อเราดูโค้ดเราจะเห็นว่ามันมีคุณสมบัติ 2 อย่างคือ - size(ด้วยตัวเข้าถึงเริ่มต้น) และisEmpty(ด้วยตัวเข้าถึงที่กำหนดเอง) แต่มันมีเพียง 1 ฟิลด์คือsize. เพื่อให้เข้าใจว่ามันมีเพียง 1 ฟิลด์ให้เราดู Java ที่เทียบเท่ากับคลาสนี้

ไปที่ Tools -> Kotlin -> Show Kotlin ByteCode ใน Android Studio คลิกที่ Decompile

   public final class DummyClass {
   private int size;

   public final int getSize() {
      return this.size;
   }

   public final void setSize(int var1) {
      this.size = var1;
   }

   public final boolean isEmpty() {
      return this.size == 0;
   }

   public final void setEmpty(boolean value) {
      this.size *= 2;
   }
}

เห็นได้ชัดว่าคลาส java มีฟังก์ชัน getter และ setter สำหรับisEmptyเท่านั้นและไม่มีฟิลด์ที่ประกาศไว้ ใน Kotlin ไม่มีฟิลด์สำรองสำหรับคุณสมบัติisEmptyเนื่องจากคุณสมบัติไม่ได้ขึ้นอยู่กับฟิลด์นั้นเลย จึงไม่มีฟิลด์สำรอง


ตอนนี้ให้เราลบ getter และ setter ของisEmptyคุณสมบัติที่กำหนดเอง

class DummyClass {
    var size = 0;
    var isEmpty = false
}

และ Java ที่เทียบเท่ากับคลาสข้างต้นคือ

public final class DummyClass {
   private int size;
   private boolean isEmpty;

   public final int getSize() {
      return this.size;
   }

   public final void setSize(int var1) {
      this.size = var1;
   }

   public final boolean isEmpty() {
      return this.isEmpty;
   }

   public final void setEmpty(boolean var1) {
      this.isEmpty = var1;
   }
}

ที่นี่เราเห็นทั้งทุ่งsizeและisEmpty. isEmptyเป็นเขตข้อมูลสำรองเนื่องจาก getter และ setter สำหรับisEmptyคุณสมบัติขึ้นอยู่กับมัน


4
คำอธิบายที่ดี ขอบคุณ
Sonu Sanjeev

1
ขอบคุณจริงๆสำหรับคำอธิบาย ฉันมาที่ Kotlin จาก Java ด้วยและแนวคิดเรื่องคุณสมบัติก็เป็นเรื่องใหม่สำหรับฉัน แต่ฉันเข้าใจแล้วขอบคุณคุณและไกด์ :)
Yamashiro Rion

พระเจ้าอาจอวยพรคุณ
Andrea Cioccarelli

ฉันชอบคำตอบนี้ที่อ้างถึงข้อเท็จจริงอย่างตรงไปตรงมา ฉันยังคงสงสัยอยู่เพราะC #ไม่จำเป็นต้องใช้fieldคีย์เวิร์ดเป็นไปได้หรือไม่ที่การปรับปรุงภาษาของ Kotlin จะลบfieldคีย์เวิร์ดแปลก ๆ นี้ออกไปและหลีกเลี่ยงไม่ให้วิญญาณที่ทำอะไรไม่ถูกตกลงไปในนรกของการเรียกซ้ำที่ไม่มีที่สิ้นสุด?
eigenfield

9

เขตข้อมูลสำรองเหมาะสำหรับการเรียกใช้การตรวจสอบความถูกต้องหรือทริกเกอร์เหตุการณ์เมื่อเปลี่ยนสถานะ นึกถึงเวลาที่คุณเพิ่มโค้ดลงใน Java setter / getter ช่องสำรองจะมีประโยชน์ในสถานการณ์ที่คล้ายคลึงกัน คุณจะใช้เขตข้อมูลสำรองเมื่อคุณต้องการควบคุมหรือมีการมองเห็นเหนือ setters / getters

เมื่อกำหนดฟิลด์ด้วยชื่อฟิลด์เองคุณจะเรียกใช้ setter (เช่นset(value)) ในตัวอย่างที่คุณมีthis.counter = valueจะเรียกคืนเป็นชุด (ค่า) จนกว่าเราจะล้นสแต็กของเรา การใช้fieldบายพาสโค้ด setter (หรือ getter)


ขออภัยคำอธิบายของคุณมีคำที่ต้องอธิบาย จากนั้นก่อนอื่นคุณอ้างถึงสถานการณ์ Java จากนั้นทันใดนั้นโดยไม่มีการเตือนว่าคุณเปลี่ยน lains เป็นคำสั่ง Kotlin จริง ความต้องการคำหลักของ Kotlin fieldไม่ได้อยู่ในC #ดังนั้นเราจึงต้องการคำอธิบายที่ดีกว่าคำที่คุณอ้างถึงที่นี่
eigenfield

2

ความเข้าใจของฉันคือการใช้ตัวระบุฟิลด์เพื่ออ้างอิงถึงค่าของคุณสมบัติในgetหรือsetเมื่อคุณต้องการเปลี่ยนหรือใช้ค่าของคุณสมบัติในgetหรือชุด

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

class A{
    var a:Int=1
        get(){return field * 2}    // Similiar to Java: public int geta(){return this.a * 2}
        set(value) {field = value + 1}
}

จากนั้น:

var t = A()
println(t.a)    // OUTPUT: 2, equal to Java code: println(t.a * 2)
t.a = 2         // The real action is similar to Java code: t.a = t.a +1
println(t.a)    // OUTPUT: 6, equal to Java code: println(t.a * 2)

0

คำศัพท์ที่backing fieldเต็มไปด้วยความลึกลับ คีย์เวิร์ดที่ใช้คือfield. get/setวิธีการดังต่อไปนี้ทันทีติดกับตัวแปรของสมาชิกที่เป็นเรื่องเกี่ยวกับที่จะได้รับหรือการตั้งค่าผ่านประตูกลไกนี้วิธีการป้องกัน fieldคำหลักเพียงหมายถึงตัวแปรสมาชิกที่จะได้รับการตั้งค่าหรือรับ ในปัจจุบัน Kotlin คุณไม่สามารถอ้างถึงตัวแปรสมาชิกได้โดยตรงภายในวิธีgetหรือset protective door เพราะมันจะส่งผลให้เกิดการเรียกซ้ำแบบไม่สิ้นสุดเนื่องจากจะเรียกใช้getหรือsetอีกครั้งและทำให้รันไทม์ลงไปในเหวลึก

ในC #คุณสามารถอ้างอิงตัวแปรสมาชิกโดยตรงภายในเมธอด getter / setter ฉันอ้างถึงการเปรียบเทียบนี้เพื่อนำเสนอแนวคิดที่ว่าfieldคีย์เวิร์ดนี้เป็นวิธีที่ Kotlin ใช้ในปัจจุบัน แต่ฉันหวังว่ามันจะถูกลบออกในเวอร์ชันที่ใหม่กว่าและอนุญาตให้เราอ้างอิงตัวแปรสมาชิกโดยตรงได้โดยตรงโดยไม่ส่งผลให้เกิดการเรียกซ้ำแบบไม่สิ้นสุด

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