Bagaimana cara mengirimkan parameter ke kelas anonim?

146

Apakah mungkin untuk melewatkan parameter, atau mengakses parameter eksternal ke kelas anonim? Sebagai contoh:

int myVariable = 1;

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

Apakah ada cara bagi pendengar untuk mengakses myVariable atau diteruskan myVariable tanpa membuat pendengar sebagai kelas bernama sebenarnya?

Lewis
sumber
7
Anda dapat referensi finalvariabel lokal dari metode melampirkan.
Tom Hawtin - tackline
Saya suka tampilan saran Adam Mmlodzinski untuk mendefinisikan metode pribadi yang menginisialisasi instance pribadi myVariable (s) dan dapat dipanggil di penjepit penutup karena kembali this.
dlamblin
Pertanyaan ini memiliki beberapa tujuan bersama: stackoverflow.com/questions/362424/…
Alastair McCormack
Anda juga dapat menggunakan variabel kelas global dari dalam kelas anonim. Mungkin tidak terlalu bersih, tetapi bisa melakukan pekerjaan.
Jori

Jawaban:

78

Secara teknis, tidak, karena kelas anonim tidak dapat memiliki konstruktor.

Namun, kelas dapat merujuk variabel dari yang berisi cakupan. Untuk kelas anonim, ini bisa berupa variabel instan dari kelas yang berisi atau variabel lokal yang ditandai sebagai final.

sunting : Seperti yang ditunjukkan Peter, Anda juga dapat meneruskan parameter ke konstruktor superclass dari kelas anonim.

Matthew Willis
sumber
21
Penggunaan kelas anonim menggunakan konstruktor dari induknya. misalnyanew ArrayList(10) { }
Peter Lawrey
Poin yang bagus. Jadi itu akan menjadi cara lain untuk meneruskan parameter ke kelas anonim, meskipun kemungkinan Anda tidak akan memiliki kontrol atas parameter itu.
Matthew Willis
kelas anonim tidak memerlukan konstruktor
newacct
4
Kelas anonim dapat memiliki Instance Initializers, yang dapat berfungsi sebagai konstruktor tanpa parameter di kelas anonim. Ini dieksekusi dalam urutan yang sama dengan penugasan lapangan, yaitu setelah super()dan sebelum sisa konstruktor yang sebenarnya. new someclass(){ fields; {initializer} fields; methods(){} }. Ini semacam penginisialisasi statis tetapi tanpa kata kunci statis. docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.6
Mark Jeronimus
Lihat ini stackoverflow.com/a/3045185/1737819 ia mengatakan bagaimana menerapkan tanpa konstruktor.
Pengembang Marius Žilėnas
336

Ya, dengan menambahkan metode penginisialisasi yang mengembalikan 'ini', dan segera memanggil metode itu:

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)  );

Tidak diperlukan deklarasi 'final'.

Adam Mlodzinski
sumber
4
wow ... brilian! Saya sangat lelah membuat finalobjek referensi supaya saya bisa mendapatkan info ke kelas anonim saya. Terima kasih sudah berbagi!
Matt Klein
7
Mengapa init()fungsinya harus kembali this? Saya benar-benar tidak mendapatkan sintaks.
Jori
11
karena myButton.addActionListener (...) Anda mengharapkan objek ActionListener sebagai objek yang dikembalikan ketika Anda memanggil metodenya.
1
Saya kira .. Saya menemukan itu agak jelek sendiri, meskipun berhasil. Saya menemukan sebagian besar waktu saya hanya mampu membuat variabel yang diperlukan dan parameter fungsi final dan langsung merujuk mereka dari kelas batin, karena biasanya mereka hanya mendapatkan dibaca.
Thomas
2
Simpler: private int anonVar = myVariable;
Anm
29

Iya. Anda dapat menangkap variabel, terlihat oleh kelas dalam. satu-satunya batasan adalah harus final

aav
sumber
Variabel instan yang dirujuk dari kelas anonim tidak harus final afaik.
Matthew Willis
8
Variabel instan dirujuk melalui thisyang bersifat final.
Peter Lawrey
Bagaimana jika saya tidak ingin variabel diubah final? Saya tidak dapat menemukan alternatif. Ini dapat memengaruhi parameter asal yang dirancang untuk menjadi final.
Alston
20

Seperti ini:

final int myVariable = 1;

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

Ini akan melakukan keajaiban

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));

sumber
8

Seperti yang ditunjukkan di http://www.coderanch.com/t/567294/java/java/declare-constructor-anonymous-class Anda dapat menambahkan instanceizer. Ini adalah blok yang tidak memiliki nama dan dieksekusi terlebih dahulu (seperti konstruktor).

Sepertinya mereka juga dibahas di inisialisasi Mengapa java Instance? dan Bagaimana penginisiasi instance berbeda dari konstruktor? membahas perbedaan dari konstruktor.

Rob Russell
sumber
Ini tidak menyelesaikan pertanyaan yang diajukan. Anda masih akan mengalami masalah dalam mengakses variabel lokal, jadi Anda harus menggunakan solusi dari Adam Mlodzinski atau adarshr
Matt Klein
1
@MattKlein Bagi saya, sepertinya itu menyelesaikannya. Sebenarnya hal yang sama, dan kurang bertele-tele.
haelix
1
Pertanyaannya ingin tahu cara meneruskan parameter ke kelas, seperti yang Anda lakukan dengan konstruktor yang membutuhkan parameter. Tautan (yang seharusnya dirangkum di sini) hanya menunjukkan bagaimana memiliki penginisialisasi instance-kurang parameter, yang tidak menjawab pertanyaan. Teknik ini dapat digunakan dengan finalvariabel seperti yang dijelaskan oleh aav, tetapi info itu tidak disediakan dalam jawaban ini. Sejauh ini, jawaban terbaik adalah yang diberikan oleh Adam Mlodzinksi (saya sekarang menggunakan pola ini secara eksklusif, tidak ada lagi final!). Saya mendukung komentar saya bahwa ini tidak menjawab pertanyaan yang diajukan.
Matt Klein
7

Solusi saya adalah menggunakan metode yang mengembalikan kelas anonim yang diimplementasikan. Argumen reguler dapat diteruskan ke metode dan tersedia dalam kelas anonim.

Misalnya: (dari beberapa kode GWT untuk menangani perubahan kotak teks):

/* 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)
        }
     };
}

Untuk contoh ini, metode kelas anonim baru akan dirujuk dengan:

textBox.addChangeHandler(newNameChangeHandler(myAxisName, myLogger))

ATAU , menggunakan persyaratan 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);
Alastair McCormack
sumber
Ini bagus, ini membatasi kelas anonim menjadi beberapa variabel yang mudah diidentifikasi dan menghilangkan kekejian karena harus membuat beberapa variabel final.
Variabel sengsara
3

Orang lain sudah menjawab bahwa kelas anonim hanya dapat mengakses variabel akhir. Tetapi mereka membiarkan pertanyaan terbuka bagaimana menjaga variabel asli non-final. Adam Mlodzinski memberikan solusi tetapi cukup kembung. Ada solusi yang lebih sederhana untuk masalah ini:

Jika Anda tidak ingin myVariablemenjadi final, Anda harus membungkusnya dalam ruang lingkup baru di mana itu tidak masalah, jika itu final.

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 tidak melakukan hal lain dalam jawabannya tetapi dengan lebih banyak kode.

ceving
sumber
Ini masih berfungsi tanpa lingkup tambahan. Secara efektif sama dengan jawaban lain menggunakan final.
Adam Mlodzinski
@AdamMlodzinski Tidak, ini sama dengan jawaban Anda, karena ia memperkenalkan variabel baru dengan nilai variabel asli dalam ruang lingkup pribadi.
ceving
Ini tidak sama secara efektif. Dalam kasus Anda, kelas batin Anda tidak dapat membuat perubahan pada anonVar - dengan demikian, efeknya berbeda. Jika kelas dalam Anda harus, katakanlah, pertahankan keadaan tertentu, kode Anda harus menggunakan semacam Object dengan setter daripada primitif.
Adam Mlodzinski
@AdamMlodzinski Bukan itu pertanyaannya. Pertanyaannya adalah bagaimana mengakses variabel luar tanpa membuat dirinya final. Dan solusinya adalah membuat salinan final. Dan tentu saja jelas bahwa seseorang dapat membuat salinan variabel yang dapat berubah yang bisa diubah dalam pendengar. Tetapi pertama-tama tidak diminta dan kedua tidak memerlukan initmetode apa pun . Saya dapat menambahkan satu baris kode tambahan ke contoh saya untuk memiliki variabel tambahan ini. Jika Anda penggemar pola pembangun merasa bebas untuk menggunakannya, tetapi mereka tidak perlu dalam hal ini.
ceving
Saya tidak melihat bagaimana ini berbeda dari menggunakan finalsolusi variabel.
Kevin Rave
3

Anda dapat menggunakan lambda biasa ("ekspresi lambda dapat menangkap variabel")

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

atau bahkan sebuah Fungsi

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

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

Menggunakan Function adalah cara yang bagus untuk refactor Dekorator dan Adaptor, lihat di sini

Saya baru saja mulai belajar tentang lambdas, jadi jika Anda menemukan kesalahan, jangan ragu untuk menulis komentar.

ZiglioUK
sumber
1

Cara sederhana untuk menaruh beberapa nilai ke variabel eksternal (bukan milik kelas anonimus) adalah bagaimana caranya!

Dengan cara yang sama jika Anda ingin mendapatkan nilai dari variabel eksternal Anda dapat membuat metode yang mengembalikan apa yang Anda inginkan!

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

    });

 }
peternakan miliknya
sumber
-2

Saya pikir kelas anonim pada dasarnya seperti lambdas tetapi dengan sintaks yang lebih buruk ... ini ternyata benar tetapi sintaksnya bahkan lebih buruk dan menyebabkan (apa yang seharusnya) variabel lokal berdarah ke dalam kelas yang mengandung.

Anda tidak dapat mengakses variabel final dengan membuatnya menjadi bidang kelas induk.

Misalnya

Antarmuka:

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

kelas:

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();
    }

Saya tidak tahu apakah mereka sudah menyelesaikan ini di java 8 (saya terjebak di dunia EE dan belum mendapat 8) tetapi dalam C # akan terlihat seperti ini:

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

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

            typeProcessor.doStuffThatUsesLambda();
        }
    }

Anda tidak perlu antarmuka terpisah di c # baik ... Saya rindu itu! Saya menemukan diri saya membuat desain yang lebih buruk di java dan mengulangi diri saya sendiri lebih karena jumlah kode + kompleksitas yang harus Anda tambahkan di java untuk menggunakan kembali sesuatu lebih buruk daripada hanya menyalin dan menempel banyak waktu.

JonnyRaa
sumber
kelihatannya seperti peretasan lain yang dapat Anda gunakan adalah memiliki satu elemen array seperti yang disebutkan di sini stackoverflow.com/a/4732586/962696
JonnyRaa