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


354
  1. aสามารถสุดท้ายได้ที่นี่ ทำไม? ฉันสามารถโอนสิทธิaในonClick()วิธีการโดยไม่ต้องทำให้มันเป็นสมาชิกเอกชน?

    private void f(Button b, final int a){
        b.addClickHandler(new ClickHandler() {
    
            @Override
            public void onClick(ClickEvent event) {
                int b = a*5;
    
            }
        });
    }
  2. ฉันจะคืนค่า5 * aเมื่อคลิกได้อย่างไร? ฉันหมายถึง,

    private void f(Button b, final int a){
        b.addClickHandler(new ClickHandler() {
    
            @Override
            public void onClick(ClickEvent event) {
                 int b = a*5;
                 return b; // but return type is void 
            }
        });
    }

1
ฉันไม่คิดว่าคลาส Java แบบไม่ระบุชื่อให้การปิดแลมบ์ดาที่คุณคาดหวัง แต่มีคนโปรดแก้ไขฉันถ้าฉันผิด ...
user541686

4
คุณพยายามทำอะไรให้สำเร็จ ตัวจัดการคลิกสามารถดำเนินการได้เมื่อ "f" เสร็จสิ้น
Ivan Dubrov

@ Lambert หากคุณต้องการใช้ a ในวิธี onClick จะต้องเป็นขั้นสุดท้าย @Ivan วิธีสามารถ f () วิธีการทำงานเช่น onClick () วิธีการกลับมา int เมื่อคลิก

2
นั่นคือสิ่งที่ฉันหมายถึง - มันไม่สนับสนุนการปิดเต็มเพราะมันไม่อนุญาตให้เข้าถึงตัวแปรที่ไม่ใช่ครั้งสุดท้าย
user541686

4
หมายเหตุ: ตั้งแต่ Java 8 ตัวแปรของคุณจะต้องสิ้นสุดอย่างมีประสิทธิภาพเท่านั้น
Peter Lawrey

คำตอบ:


489

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


มันเป็นพื้นเนื่องจากวิธีการจัดการ Java ปิด

เมื่อคุณสร้างอินสแตนซ์ของคลาสภายในที่ไม่ระบุชื่อตัวแปรใด ๆ ที่ใช้ภายในคลาสนั้นจะมีค่าที่คัดลอกมาผ่านทางตัวสร้างที่สร้างอัตโนมัติ สิ่งนี้หลีกเลี่ยงคอมไพเลอร์ที่ต้องสร้างประเภทพิเศษต่าง ๆ เพื่อเก็บสถานะโลจิคัลของ "ตัวแปรเฉพาะ" เช่นคอมไพเลอร์ C # ทำ ... (เมื่อ C # รวบรวมตัวแปรในฟังก์ชันที่ไม่ระบุชื่อจริงๆจับตัวแปร - การปิดสามารถอัปเดตตัวแปรในแบบที่เห็นได้โดยเนื้อความหลักของวิธีและในทางกลับกัน)

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

การทำให้ตัวแปรสุดท้ายลบความเป็นไปได้ทั้งหมดเหล่านี้ - เนื่องจากค่าไม่สามารถเปลี่ยนแปลงได้ทั้งหมดคุณไม่จำเป็นต้องกังวลว่าการเปลี่ยนแปลงดังกล่าวจะสามารถมองเห็นได้หรือไม่ วิธีเดียวที่จะอนุญาตให้เมธอดและคลาสภายในที่ไม่ระบุชื่อเห็นการเปลี่ยนแปลงของกันและกันคือการใช้คำอธิบายบางอย่างที่ไม่แน่นอน นี่อาจเป็นคลาสที่ล้อมรอบตัวเองอาเรย์ประเภทของ wrapper ที่ไม่แน่นอน ... อะไรอย่างนั้น โดยพื้นฐานแล้วมันค่อนข้างเหมือนกับการสื่อสารระหว่างวิธีหนึ่งและอีกวิธีหนึ่ง: การเปลี่ยนแปลงที่เกิดขึ้นกับพารามิเตอร์ของวิธีการหนึ่งจะไม่เห็นโดยผู้เรียก แต่จะเห็นการเปลี่ยนแปลงที่เกิดขึ้นกับวัตถุที่อ้างถึงโดยพารามิเตอร์

หากคุณสนใจที่จะเปรียบเทียบรายละเอียดเพิ่มเติมระหว่าง Java และ C # closures ฉันมีบทความที่จะอธิบายเพิ่มเติม ฉันต้องการเน้นด้าน Java ในคำตอบนี้ :)


4
@Ivan: เช่น C # โดยทั่วไป มันมาพร้อมกับระดับความซับซ้อนที่ยุติธรรมหากคุณต้องการฟังก์ชันการทำงานแบบเดียวกับ C # ซึ่งตัวแปรจากขอบเขตที่แตกต่างกันสามารถ "อินสแตนซ์" จำนวนครั้งที่แตกต่างกัน
Jon Skeet


11
ทั้งหมดนี้เป็นจริงสำหรับ Java 7 โปรดจำไว้ว่าด้วย Java 8 มีการปิดและตอนนี้ก็เป็นไปได้ที่จะเข้าถึงฟิลด์ที่ไม่สิ้นสุดของคลาสจากคลาสภายใน
งัด Bader

22
@MathiasBader: จริงเหรอ? ฉันคิดว่ามันยังคงเป็นกลไกเดียวกันโดยพื้นฐานแล้วคอมไพเลอร์ตอนนี้ฉลาดพอที่จะสรุปได้final(แต่มันยังคงต้องมีประสิทธิภาพขั้นสุดท้าย)
Thilo

3
@Mathias Bader: คุณมักจะเข้าถึงไม่ใช่สุดท้ายอาจสาขาที่ไม่ต้องวุ่นวายกับท้องถิ่นตัวแปรซึ่งจะเป็นที่สิ้นสุดและยังคงต้องสุดท้ายได้อย่างมีประสิทธิภาพดังนั้น Java 8 ไม่ได้เปลี่ยนความหมาย
Holger

41

มีเคล็ดลับที่อนุญาตให้คลาสที่ไม่ระบุชื่อสามารถอัปเดตข้อมูลในขอบเขตด้านนอกได้

private void f(Button b, final int a) {
    final int[] res = new int[1];
    b.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            res[0] = a * 5;
        }
    });

    // But at this point handler is most likely not executed yet!
    // How should we now res[0] is ready?
}

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

เคล็ดลับนี้ใช้งานได้ แต่ถ้าคลาสที่ไม่ระบุชื่อถูกเรียกใช้ในเธรดเดียวกันทันที ชอบ:

// ...

final int[] res = new int[1];
Runnable r = new Runnable() { public void run() { res[0] = 123; } };
r.run();
System.out.println(res[0]);

// ...

2
ขอบคุณสำหรับคำตอบ. ฉันรู้ทั้งหมดนี้และทางออกของฉันดีกว่านี้ คำถามของฉันคือ "ทำไมเพียง แต่สุดท้าย"

6
คำตอบแล้วว่าเป็นวิธีการที่พวกเขาจะดำเนิน :)
อีวาน Dubrov

1
ขอบคุณ ฉันใช้เคล็ดลับข้างต้นด้วยตัวเอง ฉันไม่แน่ใจว่ามันเป็นความคิดที่ดีหรือไม่ หาก Java ไม่อนุญาตอาจเป็นเหตุผลที่ดี คำตอบของคุณชี้แจงว่าList.forEachรหัสของฉันปลอดภัย
RuntimeException

อ่านstackoverflow.com/q/12830611/2073130เพื่อการอภิปรายที่ดีเกี่ยวกับเหตุผลเบื้องหลัง "ทำไมจึงเป็นเพียงขั้นสุดท้าย"
lcn

มีวิธีแก้ไขหลายวิธี ของฉันคือ: resf สุดท้าย int = res; ตอนแรกฉันใช้วิธีการเรียงลำดับ แต่ฉันคิดว่ามันมีไวยากรณ์ที่ยุ่งยากเกินไป AtomicReference อาจจะช้ากว่าเล็กน้อย (จัดสรรวัตถุ)
zakmck

17

คลาสที่ไม่ระบุชื่อเป็นคลาสภายในและกฎที่เข้มงวดใช้กับคลาสภายใน (JLS 8.1.3) :

ตัวแปรท้องถิ่นใด ๆ อย่างเป็นทางการพารามิเตอร์วิธีจัดการข้อยกเว้นหรือพารามิเตอร์ที่ใช้ แต่ไม่ได้ประกาศในระดับชั้นจะต้องประกาศเป็นครั้งสุดท้าย ตัวแปรท้องถิ่นใด ๆ ใช้ แต่ไม่ได้ประกาศในระดับชั้นจะต้องกำหนดแน่นอนก่อนที่ร่างของระดับชั้นใน

ฉันยังไม่พบเหตุผลหรือคำอธิบายเกี่ยวกับ jls หรือ jvms แต่เรารู้แล้วว่าคอมไพเลอร์สร้างไฟล์คลาสแยกต่างหากสำหรับแต่ละคลาสภายในและต้องแน่ใจว่าวิธีที่ประกาศไว้ในไฟล์คลาสนี้ ( ในระดับรหัสไบต์) อย่างน้อยมีการเข้าถึงค่าของตัวแปรท้องถิ่น

( จอนมีคำตอบที่สมบูรณ์ - ฉันยกเลิกการลบทิ้งเพราะอาจสนใจกฎ JLS)


11

คุณสามารถสร้างตัวแปรระดับชั้นเรียนเพื่อรับค่าที่ส่งคืน ฉันหมายถึง

class A {
    int k = 0;
    private void f(Button b, int a){
        b.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            k = a * 5;
        }
    });
}

ตอนนี้คุณสามารถรับค่า K และใช้งานได้ตามที่คุณต้องการ

คำตอบว่าทำไม:

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

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


ฉันพูดถึงว่า "โดยไม่ทำให้สมาชิกเป็นส่วนตัว"

6

ใน Java แล้วตัวแปรสามารถสุดท้ายไม่เพียง แต่เป็นพารามิเตอร์ แต่เป็นเขตข้อมูลระดับเช่น

public class Test
{
 public final int a = 3;

หรือเป็นตัวแปรท้องถิ่นเช่น

public static void main(String[] args)
{
 final int a = 3;

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

public class Test
{
 public int a;
 public void doSomething()
 {
  Runnable runnable =
   new Runnable()
   {
    public void run()
    {
     System.out.println(a);
     a = a+1;
    }
   };
 }
}

คุณไม่สามารถมีตัวแปรเป็นค่าสุดท้ายและให้ค่าใหม่ finalหมายถึงเพียงแค่ว่า: ค่าไม่สามารถเปลี่ยนได้และสุดท้าย

และเนื่องจากเป็นขั้นสุดท้าย Java จึงสามารถคัดลอกไปยังคลาสที่ไม่ระบุชื่อได้ คุณไม่ได้รับการอ้างอิงถึง int (โดยเฉพาะอย่างยิ่งเนื่องจากคุณไม่สามารถมีการอ้างอิงถึง primitives เช่น int ใน Java, เพียงการอ้างอิงถึงObjects )

มันแค่คัดลอกค่าของ a ลงใน int โดยนัยที่เรียกว่า a ในคลาสที่ไม่ระบุชื่อของคุณ


3
ฉันเชื่อมโยง "ชั้นระดับตัวแปร" staticกับ อาจจะชัดเจนกว่านี้ถ้าคุณใช้ "ตัวแปรอินสแตนซ์" แทน
eljenso

1
ดีฉันใช้ระดับชั้นเรียนเพราะเทคนิคจะทำงานกับทั้งอินสแตนซ์และตัวแปรแบบคงที่
Zach L

เรารู้แล้วว่าขั้นตอนสุดท้ายนั้นสามารถเข้าถึงได้ แต่เราต้องการรู้ว่าทำไม คุณช่วยเพิ่มคำอธิบายเพิ่มเติมเกี่ยวกับสาเหตุด้านข้างได้ไหม
Saurabh Oza

6

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


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

Mike76 ฉันไม่ได้ดูที่การดำเนินงาน C # 's แต่ Scala ไม่สิ่งที่สองที่คุณกล่าวถึงผมคิดว่า: ถ้ามีIntจะถูกกำหนดให้อยู่ภายในการปิดการเปลี่ยนแปลงตัวแปรอินสแตนซ์ของIntRef(หลักแน่นอนIntegerเสื้อคลุม) การเข้าถึงตัวแปรทุกครั้งจะถูกเขียนใหม่ตามนั้น
Adowrath

3

เพื่อทำความเข้าใจเหตุผลสำหรับข้อ จำกัด นี้ให้พิจารณาโปรแกรมต่อไปนี้:

public class Program {

    interface Interface {
        public void printInteger();
    }
    static Interface interfaceInstance = null;

    static void initialize(int val) {
        class Impl implements Interface {
            @Override
            public void printInteger() {
                System.out.println(val);
            }
        }
        interfaceInstance = new Impl();
    }

    public static void main(String[] args) {
        initialize(12345);
        interfaceInstance.printInteger();
    }
}

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


2

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


2

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

( http://en.wikipedia.org/wiki/Final_%28Java%29 )


1
private void f(Button b, final int a[]) {

    b.addClickHandler(new ClickHandler() {

        @Override
        public void onClick(ClickEvent event) {
            a[0] = a[0] * 5;

        }
    });
}

0

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

พิจารณากรณีการใช้งานที่ลูกแกะของคุณแทนที่จะถูกนำไปใช้จะถูกเก็บไว้ในบางสถานที่และเรียกใช้ในภายหลัง

ฉันจำได้ว่าใน Smalltalk คุณจะได้รับร้านค้าที่ผิดกฎหมายเมื่อคุณทำการปรับเปลี่ยนเช่นนั้น


0

ลองรหัสนี้

สร้าง Array List แล้วใส่ค่าเข้าไปข้างในแล้วคืนมัน

private ArrayList f(Button b, final int a)
{
    final ArrayList al = new ArrayList();
    b.addClickHandler(new ClickHandler() {

         @Override
        public void onClick(ClickEvent event) {
             int b = a*5;
             al.add(b);
        }
    });
    return al;
}

OP ขอเหตุผลว่าทำไมบางสิ่งจำเป็น ดังนั้นคุณควรชี้ให้เห็นว่าโค้ดของคุณจัดการกับมันอย่างไร
NitinSingh

0

คลาสที่ไม่ระบุชื่อของ Java นั้นคล้ายกับการปิด Javascript แต่ Java นั้นนำไปใช้ในวิธีที่ต่างกัน (ตรวจสอบคำตอบของ Andersen)

ดังนั้นเพื่อไม่ให้นักพัฒนา Java สับสนกับพฤติกรรมแปลก ๆ ที่อาจเกิดขึ้นสำหรับผู้ที่มาจากพื้นหลัง Javascript ฉันเดาว่านั่นเป็นเหตุผลที่พวกเขาบังคับให้เราใช้finalนี่ไม่ใช่ข้อ จำกัด JVM

ลองดูตัวอย่าง Javascript ด้านล่าง:

var add = (function () {
  var counter = 0;

  var func = function () {
    console.log("counter now = " + counter);
    counter += 1; 
  };

  counter = 100; // line 1, this one need to be final in Java

  return func;

})();


add(); // this will print out 100 in Javascript but 0 in Java

ใน Javascript counterค่าจะเป็น 100 เนื่องจากมีเพียงcounterตัวแปรเดียวตั้งแต่ต้นจนจบ

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

ดังนั้นการเปลี่ยนแปลงใด ๆ หลังจากการสร้างวัตถุภายใน (เช่นบรรทัดที่ 1) มันจะไม่ส่งผลกระทบต่อวัตถุภายใน ดังนั้นจะสร้างความสับสนระหว่างผลลัพธ์และพฤติกรรมที่แตกต่างกันสองรายการ (ระหว่าง Java และ Javascript)

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


0

ตัวแปรสุดท้ายของ Java ภายในคลาสภายใน

ชั้นในสามารถใช้งานได้เท่านั้น

  1. การอ้างอิงจากคลาสภายนอก
  2. ตัวแปรท้องถิ่นสุดท้ายจากนอกขอบเขตซึ่งเป็นประเภทการอ้างอิง (เช่นObject... )
  3. ค่า (ดั้งเดิม) (เช่นint... ) ประเภทสามารถห่อด้วยประเภทอ้างอิงสุดท้าย IntelliJ IDEAสามารถช่วยคุณแปลงมันให้เป็นหนึ่งองค์ประกอบอาร์เรย์

เมื่อnon static nested( inner class) [เกี่ยวกับ]ถูกสร้างขึ้นโดยคอมไพเลอร์ - คลาสใหม่ - <OuterClass>$<InnerClass>.classถูกสร้างขึ้นและพารามิเตอร์ที่ถูกผูกไว้จะถูกส่งผ่านเข้ามาในคอนสตรัค [ตัวแปรท้องถิ่นในกอง] มันคล้ายกับการปิด

ตัวแปรสุดท้ายคือตัวแปรที่ไม่สามารถกำหนดใหม่ได้ ตัวแปรอ้างอิงสุดท้ายยังสามารถเปลี่ยนแปลงได้โดยการแก้ไขสถานะ

มันเป็นไปได้ที่มันจะแปลกเพราะในฐานะโปรแกรมเมอร์คุณสามารถทำสิ่งนี้ได้

//Not possible 
private void foo() {

    MyClass myClass = new MyClass(); //address 1
    int a = 5;

    Button button = new Button();

    //just as an example
    button.addClickHandler(new ClickHandler() {


        @Override
        public void onClick(ClickEvent event) {

            myClass.something(); //<- what is the address ?
            int b = a; //<- 5 or 10 ?

            //illusion that next changes are visible for Outer class
            myClass = new MyClass();
            a = 15;
        }
    });

    myClass = new MyClass(); //address 2
    int a = 10;
}

-2

อาจเป็นเคล็ดลับนี้ให้ความคิดของคุณ

Boolean var= new anonymousClass(){
    private String myVar; //String for example
    @Overriden public Boolean method(int i){
          //use myVar and i
    }
    public String setVar(String var){myVar=var; return this;} //Returns self instane
}.setVar("Hello").method(3);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.