จะส่งพารามิเตอร์ไปยังคลาสที่ไม่ระบุชื่อได้อย่างไร


146

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

int myVariable = 1;

myButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // How would one access myVariable here?
    }
});

มีวิธีใดบ้างสำหรับผู้ฟังที่เข้าถึง myVariable หรือผ่าน myVariable โดยไม่ต้องสร้าง listener เป็นคลาสที่มีชื่อจริง?


7
คุณสามารถอ้างอิงfinalตัวแปรโลคัลได้จากวิธีการปิดล้อม
Tom Hawtin - tackline

ฉันชอบรูปลักษณ์ของข้อเสนอแนะของอดัม Mmlodzinski ของการกำหนดวิธีการเอกชนที่เริ่มต้นเช่น myVariable ส่วนตัว (s) thisและสามารถเรียกว่าที่รั้งปิดเนื่องจากการกลับมา
dlamblin

คำถามนี้มีเป้าหมายร่วมกันบางส่วนของ: stackoverflow.com/questions/362424/…
Alastair McCormack

คุณยังสามารถใช้ตัวแปรคลาสโกลบอลจากภายในคลาสนิรนาม อาจไม่สะอาดมาก แต่ก็สามารถทำงานได้
Jori

คำตอบ:


78

ในทางเทคนิคไม่เพราะคลาสนิรนามไม่สามารถมีคอนสตรัคเตอร์ได้

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

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


21
คลาสแบบไม่ระบุชื่อใช้ตัวสร้างของพาเรนต์ เช่นnew ArrayList(10) { }
Peter Lawrey

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

คลาสนิรนามไม่ต้องการตัวสร้าง
newacct

4
คลาสที่ไม่ระบุชื่อสามารถมีอินสแตนซ์เริ่มต้นซึ่งสามารถทำหน้าที่เป็นตัวสร้างพารามิเตอร์น้อยในคลาสที่ไม่ระบุชื่อ สิ่งเหล่านี้จะถูกดำเนินการในลำดับเดียวกับการกำหนดเขตข้อมูลเช่นหลังจากsuper()และก่อนส่วนที่เหลือของตัวสร้างที่แท้จริง new someclass(){ fields; {initializer} fields; methods(){} }. มันเป็นเหมือนตัวเริ่มต้นคงที่ แต่ไม่มีคำหลักคงที่ docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.6
Mark Jeronimus

ดูstackoverflow.com/a/3045185/1737819นี้ซึ่งระบุว่าจะใช้งานอย่างไรโดยไม่ต้องมี Constructor
พัฒนา Marius Žilėnas

336

ใช่โดยเพิ่มเมธอด initializer ที่ส่งคืน 'this' และเรียกเมธอดนั้นทันที:

int myVariable = 1;

myButton.addActionListener(new ActionListener() {
    private int anonVar;
    public void actionPerformed(ActionEvent e) {
        // How would one access myVariable here?
        // It's now here:
        System.out.println("Initialized with value: " + anonVar);
    }
    private ActionListener init(int var){
        anonVar = var;
        return this;
    }
}.init(myVariable)  );

ไม่มีการประกาศ 'ขั้นสุดท้าย' ที่จำเป็น


4
ว้าว ... เยี่ยมเลย! ฉันเบื่อที่จะสร้างfinalออบเจ็กต์อ้างอิงเพื่อที่จะได้รับข้อมูลในคลาสที่ไม่ระบุชื่อ ขอบคุณสำหรับการแชร์!
Matt Klein

7
ทำไมinit()ฟังก์ชั่นจึงต้องกลับมาthis? ฉันไม่ได้รับไวยากรณ์จริงๆ
Jori

11
เนื่องจาก myButton.addActionListener (... ) ของคุณต้องการวัตถุ ActionListener เป็นวัตถุซึ่งถูกส่งคืนเมื่อคุณเรียกใช้เมธอด

1
ฉันเดา .. ฉันพบว่าตัวเองค่อนข้างน่าเกลียดแม้ว่ามันจะใช้งานได้ ฉันพบว่าเวลาส่วนใหญ่ฉันสามารถที่จะทำให้ตัวแปรที่จำเป็นและพารามิเตอร์ฟังก์ชั่นสุดท้ายและอ้างอิงโดยตรงจากชั้นในเพราะมักจะเป็นเพียงการอ่าน
โทมัส

2
ง่ายกว่า: private int anonVar = myVariable;
Anm

29

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


ตัวแปรอินสแตนซ์ที่อ้างอิงจากคลาสที่ไม่ระบุชื่อไม่จำเป็นต้องเป็น afaik สุดท้าย
Matthew Willis

8
ตัวแปรอินสแตนซ์ถูกอ้างอิงผ่านทางthisซึ่งถือเป็นที่สิ้นสุด
Peter Lawrey

ถ้าฉันไม่ต้องการให้ตัวแปรเปลี่ยนเป็นfinalอะไร ฉันหาทางเลือกอื่นไม่พบ finalนี้อาจมีผลต่อพารามิเตอร์กำเนิดซึ่งถูกออกแบบมาให้
Alston

20

แบบนี้:

final int myVariable = 1;

myButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // Now you can access it alright.
    }
});

14

สิ่งนี้จะทำเวทย์มนตร์

int myVariable = 1;

myButton.addActionListener(new ActionListener() {

    int myVariable;

    public void actionPerformed(ActionEvent e) {
        // myVariable ...
    }

    public ActionListener setParams(int myVariable) {

        this.myVariable = myVariable;

        return this;
    }
}.setParams(myVariable));

8

ดังที่แสดงไว้ที่http://www.coderanch.com/t/567294/java/java/declare-constructor-anonymous-classคุณสามารถเพิ่ม initializer อินสแตนซ์ได้ มันเป็นบล็อกที่ไม่มีชื่อและถูกเรียกใช้ก่อน (เหมือนตัวสร้าง)

ดูเหมือนว่าพวกเขากำลังพูดถึงที่ทำไม initializer java Instance? และinitializer อินสแตนซ์ต่างจากตัวสร้างได้อย่างไร? กล่าวถึงความแตกต่างจากตัวสร้าง


นี่ไม่ได้แก้คำถามที่ถาม คุณจะยังคงมีปัญหาในการเข้าถึงตัวแปรท้องถิ่นดังนั้นคุณจะต้องใช้วิธีแก้ปัญหาจาก Adam Mlodzinski หรือ adarshr
Matt Klein

1
@ MattKlein สำหรับฉันมันดูเหมือนว่าจะแก้ปัญหาได้ มันเป็นสิ่งเดียวกันจริง ๆ และ verbose น้อยลง
haelix

1
คำถามอยากรู้วิธีส่งพารามิเตอร์ไปยังคลาสเช่นเดียวกับที่คุณใช้กับคอนสตรัคเตอร์ที่ต้องการพารามิเตอร์ ลิงก์ (ซึ่งควรสรุปไว้ที่นี่) จะแสดงเฉพาะวิธีการเริ่มต้นอินสแตนซ์ที่ไม่มีพารามิเตอร์ซึ่งไม่ตอบคำถาม เทคนิคนี้สามารถใช้กับfinalตัวแปรตามที่อธิบายโดย aav แต่ข้อมูลนั้นไม่ได้ให้ไว้ในคำตอบนี้ เท่าที่ผ่านมาคำตอบที่ดีที่สุดคือคำตอบที่ Adam Mlodzinksi (ตอนนี้ฉันใช้รูปแบบนี้โดยเฉพาะไม่มีรอบชิงชนะเลิศอีกต่อไป!) ฉันยืนตามความคิดเห็นของฉันว่าไม่ได้ตอบคำถามที่ถาม
Matt Klein

7

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

ตัวอย่างเช่น: (จากรหัส GWT บางอย่างเพื่อจัดการการเปลี่ยนแปลงกล่องข้อความ):

/* Regular method. Returns the required interface/abstract/class
   Arguments are defined as final */
private ChangeHandler newNameChangeHandler(final String axisId, final Logger logger) {

    // Return a new anonymous class
    return new ChangeHandler() {
        public void onChange(ChangeEvent event) {
            // Access method scope variables           
            logger.fine(axisId)
        }
     };
}

สำหรับตัวอย่างนี้เมธอด class anonymous ใหม่จะถูกอ้างอิงด้วย:

textBox.addChangeHandler(newNameChangeHandler(myAxisName, myLogger))

หรือใช้ข้อกำหนดของ OP:

private ActionListener newActionListener(final int aVariable) {
    return new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.out.println("Your variable is: " + aVariable);
        }
    };
}
...
int myVariable = 1;
newActionListener(myVariable);

นี่เป็นการดีมัน จำกัด คลาสนิรนามให้กับตัวแปรที่ง่ายต่อการระบุและกำจัดสิ่งที่น่ารังเกียจที่ต้องทำให้ตัวแปรบางตัวเป็นที่สิ้นสุด
แปรปรวนแปรปรวน

3

คนอื่นตอบแล้วว่าคลาสนิรนามสามารถเข้าถึงตัวแปรสุดท้ายเท่านั้น แต่พวกเขาปล่อยให้คำถามเปิดวิธีการเก็บตัวแปรเดิมไม่เป็นที่สิ้นสุด อดัม Mlodzinskiให้ทางออก แต่มีป่องสวย มีวิธีแก้ไขปัญหาที่ง่ายกว่ามาก:

หากคุณไม่ต้องการที่myVariableจะเป็นที่สิ้นสุดคุณต้องห่อมันในขอบเขตใหม่ที่มันไม่สำคัญถ้ามันเป็นครั้งสุดท้าย

int myVariable = 1;

{
    final int anonVar = myVariable;

    myButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            // How would one access myVariable here?
            // Use anonVar instead of myVariable
        }
    });
}

Adam Mlodzinski ไม่ได้ทำอะไรอย่างอื่นในคำตอบของเขา แต่มีรหัสมากขึ้น


ยังคงใช้งานได้โดยไม่มีขอบเขตเพิ่มเติม มันมีประสิทธิภาพเช่นเดียวกับคำตอบอื่น ๆ ที่ใช้สุดท้าย
Adam Mlodzinski

@AdamMlodzinski ไม่เหมือนคำตอบของคุณอย่างมีประสิทธิภาพเพราะมันแนะนำตัวแปรใหม่ที่มีค่าของตัวแปรดั้งเดิมในขอบเขตส่วนตัว
ceving

มันไม่ได้มีประสิทธิภาพเหมือนกัน ในกรณีของคุณคลาสภายในของคุณไม่สามารถเปลี่ยนแปลง anonVar ได้ดังนั้นผลจะแตกต่างกัน ถ้าคลาสภายในของคุณต้องพูดรักษาบางสถานะโค้ดของคุณจะต้องใช้ Object กับ setter แทนที่จะเป็นแบบดั้งเดิม
Adam Mlodzinski

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

ฉันไม่เห็นว่าสิ่งนี้แตกต่างจากการใช้finalโซลูชันแปรผันอย่างไร
Kevin Rave

3

คุณสามารถใช้lambdas ธรรมดา ("แลมบ์ดานิพจน์สามารถจับภาพตัวแปร")

int myVariable = 1;
ActionListener al = ae->System.out.println(myVariable);
myButton.addActionListener( al );

หรือแม้แต่ฟังก์ชั่น

Function<Integer,ActionListener> printInt = 
    intvar -> ae -> System.out.println(intvar);

int myVariable = 1;
myButton.addActionListener( printInt.apply(myVariable) );

การใช้ฟังก์ชั่นเป็นวิธีที่ดีในการปรับโครงสร้างมัณฑนากรและอะแดปเตอร์ดูที่นี่

ฉันเพิ่งเริ่มเรียนรู้เกี่ยวกับลูกแกะดังนั้นหากคุณพบข้อผิดพลาดคุณสามารถเขียนความคิดเห็นได้


1

วิธีง่ายๆในการใส่ค่าลงในตัวแปรภายนอก (ไม่ได้อยู่ในคลาสที่ไม่ระบุชื่อ) เป็นวิธีการที่ folow!

ในทำนองเดียวกันถ้าคุณต้องการรับค่าของตัวแปรภายนอกคุณสามารถสร้างวิธีที่คืนสิ่งที่คุณต้องการ!

public class Example{

    private TypeParameter parameter;

    private void setMethod(TypeParameter parameter){

        this.parameter = parameter;

    }

    //...
    //into the anonymus class
    new AnonymusClass(){

        final TypeParameter parameterFinal = something;
        //you can call setMethod(TypeParameter parameter) here and pass the
        //parameterFinal
        setMethod(parameterFinal); 

        //now the variable out the class anonymus has the value of
        //of parameterFinal

    });

 }

-2

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

คุณสามารถเข้าถึงตัวแปรสุดท้ายโดยไม่ทำให้ตัวแปรเหล่านั้นเป็นฟิลด์ของคลาสพาเรนต์

เช่น

อินเตอร์เฟซ:

public interface TextProcessor
{
    public String Process(String text);
}

ระดับ:

private String _key;

public String toJson()
{
    TextProcessor textProcessor = new TextProcessor() {
        @Override
        public String Process(String text)
        {
            return _key + ":" + text;
        }
    };

    JSONTypeProcessor typeProcessor = new JSONTypeProcessor(textProcessor);

    foreach(String key : keys)
    {
        _key = key;

        typeProcessor.doStuffThatUsesLambda();
    }

ฉันไม่รู้ว่าพวกเขาได้แยกออกมาใน java 8 (ฉันติดอยู่ในโลก EE และยังไม่ถึง 8) แต่ใน C # มันจะมีลักษณะเช่นนี้:

    public string ToJson()
    {
        string key = null;
        var typeProcessor = new JSONTypeProcessor(text => key + ":" + text);

        foreach (var theKey in keys)
        {
            key = theKey;

            typeProcessor.doStuffThatUsesLambda();
        }
    }

คุณไม่ต้องการอินเทอร์เฟซแบบแยกใน c # เช่นกัน ... ฉันคิดถึงมัน! ฉันพบว่าตัวเองมีการออกแบบที่แย่ลงในจาวาและทำซ้ำตัวเองมากขึ้นเนื่องจากจำนวนของรหัส + ความซับซ้อนที่คุณต้องเพิ่มในจาวาเพื่อนำสิ่งที่เลวร้ายกลับมาใช้ซ้ำยิ่งกว่าการคัดลอกและวางเป็นจำนวนมาก


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