Parametreler anonim sınıfa nasıl aktarılır?


147

Anonim bir sınıfa parametreler iletmek veya harici parametrelere erişmek mümkün mü? Örneğin:

int myVariable = 1;

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

Dinleyicinin, dinleyiciyi gerçek bir adlandırılmış sınıf olarak oluşturmadan myVariable'a erişmesinin veya myVariable'ı geçirmesinin herhangi bir yolu var mı?


7
finalÇevreleyen yöntemden yerel değişkenlere başvurabilirsiniz .
Tom Hawtin - tackline

Adam Mmlodzinski'nin özel myVariable örnek (ler) i başlatan ve geri dönme nedeniyle kapanış parantezinde çağrılabilen özel bir yöntem tanımlama önerisinin görünümünü beğendim this.
dlamblin

Bu sorunun bazı paylaşılan hedefleri var: stackoverflow.com/questions/362424/…
Alastair McCormack

Anonim sınıfın içinden de global sınıf değişkenlerini kullanabilirsiniz. Belki çok temiz değil ama işi yapabilir.
Jori

Yanıtlar:


78

Teknik olarak hayır, çünkü anonim sınıfların kurucuları olamaz.

Bununla birlikte, sınıflar, içeren kapsamlardan değişkenlere başvurabilir. Anonim bir sınıf için bunlar, içeren sınıflardan örnek değişkenler veya son olarak işaretlenmiş yerel değişkenler olabilir.

düzenleme : Peter'ın da belirttiği gibi, parametreleri anonim sınıfın üst sınıfının yapıcısına da aktarabilirsiniz.


21
Anonim bir sınıf, üst sınıfın yapıcılarını kullanır. örn.new ArrayList(10) { }
Peter Lawrey

İyi bir nokta. Bu, anonim bir sınıfa bir parametre aktarmanın başka bir yolu olsa da, muhtemelen bu parametre üzerinde kontrolünüz olmayacaktır.
Matthew Willis

anonim sınıfların yapıcılara ihtiyacı yoktur
newacct

4
Anonim sınıflar, anonim sınıflarda parametresiz yapıcılar olarak işlev görebilen Örnek Başlatıcılara sahip olabilir. Bunlar, saha atamalarıyla aynı sırada, yani super()gerçek kurucunun geri kalanından sonra ve önce yürütülür . new someclass(){ fields; {initializer} fields; methods(){} }. Statik bir başlatıcıya benzer, ancak static anahtar sözcüğü yoktur. docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.6
Mark Jeronimus

Bu stackoverflow.com/a/3045185/1737819'a bakın , yapıcı olmadan nasıl uygulanacağını söylüyor.
Geliştirici Marius Žilėnas

344

Evet, 'this' döndüren bir başlatıcı yöntemi ekleyerek ve hemen bu yöntemi çağırarak:

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

'Nihai' beyana gerek yok.


5
vay ... harika! finalAnonim sınıflarıma bilgi alabilmek için bir referans nesnesi oluşturmaktan çok yoruldum . Paylaşım için teşekkürler!
Matt Klein

7
init()İşlevin neden geri dönmesi gerekiyor this? Sözdizimini gerçekten anlamıyorum.
Jori

11
çünkü myButton.addActionListener (...), bir ActionListener nesnesini, yöntemini çağırdığınızda döndürülen bir nesne olarak bekler.

1
Sanırım .. İşe yarasa da ben bunu oldukça çirkin buluyorum. Çoğu zaman gerekli değişkenleri ve fonksiyon parametrelerini son haline getirmeyi ve onlara doğrudan iç sınıftan referans vermeyi göze alabildiğimi görüyorum, çünkü bunlar genellikle sadece okunuyor.
Thomas

2
Daha basit: private int anonVar = myVariable;
Anm

30

Evet. iç sınıfa görünür olan değişkeni yakalayabilirsiniz. tek sınırlama, nihai olması gerektiğidir


Anonim bir sınıftan referans verilen örnek değişkenlerinin nihai afaik olması gerekmez.
Matthew Willis

8
Örnek değişkenleri this, nihai olan aracılığıyla referans alınır.
Peter Lawrey

Ya değişkenin olarak değiştirilmesini istemiyorsam final? Herhangi bir alternatif bulamıyorum. Bu, tasarlanmış olan menşe parametresini etkileyebilir final.
Alston

20

Bunun gibi:

final int myVariable = 1;

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

14

Bu sihri yapacak

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 adresinde gösterildiği gibi bir örnek başlatıcı ekleyebilirsiniz. Adı olmayan ve önce çalıştırılan bir bloktur (bir kurucu gibi).

Görünüşe göre neden java Örneği başlatıcılar da tartışılıyor. ve Bir örnek başlatıcının bir yapıcıdan farkı nedir? Yapıcılardan farklılıkları tartışır.


Bu, sorulan soruyu çözmez. Hala yerel değişkenlere erişim sorunu yaşayacaksınız, bu nedenle ya Adam Mlodzinski'nin çözümünü kullanmanız gerekecek ya da adarshr
Matt Klein

1
@MattKlein Bana göre, çözmüş gibi görünüyor. Aslında aynı şey ve daha az ayrıntılı.
haelix

1
Soru, parametre gerektiren bir kurucuda yapacağınız gibi, parametreleri sınıfa nasıl geçireceğinizi bilmek istiyordu. Bağlantı (burada özetlenmiş olması gerekirdi) yalnızca soruyu yanıtlamayan parametresiz bir örnek başlatıcıya nasıl sahip olunacağını gösterir. Bu teknik, finalaav tarafından açıklanan değişkenlerle kullanılabilir , ancak bu bilgi bu yanıtta verilmemiştir. Şimdiye kadar en iyi cevap Adam Mlodzinksi tarafından verilen cevaptır (şimdi bu modeli sadece kullanıyorum, final yok!). Sorulan soruyu yanıtlamadığına dair yorumumun arkasında duruyorum.
Matt Klein

7

Benim çözümüm, uygulanan anonim sınıfı döndüren bir yöntem kullanmak. Normal argümanlar yönteme geçirilebilir ve anonim sınıf içinde kullanılabilir.

Örneğin: (Metin kutusu değişikliğini işlemek için bazı GWT kodlarından):

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

Bu örnek için, yeni anonim sınıf yöntemine şununla başvurulacaktır:

textBox.addChangeHandler(newNameChangeHandler(myAxisName, myLogger))

VEYA OP'nin gerekliliklerini kullanarak:

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

Bu iyidir, anonim sınıfı birkaç tanımlaması kolay değişkenle sınırlar ve bazı değişkenleri son haline getirme zorunluluğunu ortadan kaldırır.
Miserable Variable

3

Diğer insanlar, anonim sınıfların yalnızca son değişkenlere erişebileceğini zaten yanıtladı. Ancak asıl değişkeni nihai olmayan şekilde nasıl koruyacakları sorusunu açık bırakırlar. Adam Mlodzinski bir çözüm sundu ama oldukça şişkin. Sorun için çok daha basit bir çözüm var:

myVariableNihai olmak istemiyorsanız , son olup olmadığı önemli olmayan yeni bir kapsamda sarmalamanız gerekir.

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 cevabında başka bir şey yapmıyor, çok daha fazla kodla.


Bu hala ekstra kapsam olmadan çalışıyor. Final kullanan diğer cevaplarla aynıdır.
Adam Mlodzinski

@AdamMlodzinski Hayır, cevabınızla aynıdır, çünkü özel bir kapsamda orijinal değişkenin değerine sahip yeni bir değişken sunar.
2013

Etkili olarak aynı değil. Sizin durumunuzda, iç sınıfınız anonVar'da değişiklik yapamaz - dolayısıyla etki farklıdır. Diyelim ki iç sınıfınız bir durumu sürdürmek zorundaysa, kodunuzun bir ilkelden ziyade bir ayarlayıcıya sahip bir tür Nesne kullanması gerekir.
Adam Mlodzinski

@AdamMlodzinski Soru bu değildi. Soru, kendisini nihai hale getirmeden dış değişkene nasıl erişileceğiydi. Çözüm, son bir kopya çıkarmaktır. Ve tabii ki dinleyicide değişkenin ek bir değişken kopyasının yapılabileceği açıktır. Ama önce sorulmadı ve ikincisi herhangi bir inityöntem gerektirmiyor . Bu ek değişkene sahip olmak için örneğime bir ek kod satırı ekleyebilirim. Büyük bir inşaatçı desen hayranıysanız, bunları kullanmaktan çekinmeyin, ancak bu durumda gerekli değildir.
2013

Bunun finaldeğişken çözüm kullanmaktan ne kadar farklı olduğunu anlamıyorum .
Kevin Rave

3

Düz lambdalar kullanabilirsiniz ("lambda ifadeleri değişkenleri yakalayabilir")

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

hatta bir İşlev

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

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

Fonksiyonu kullanmak, Dekoratörleri ve Adaptörleri yeniden düzenlemek için harika bir yoldur, buraya bakın

Lambdalar hakkında yeni bir şeyler öğrenmeye başladım, bu yüzden bir hata görürseniz, yorum yazmaktan çekinmeyin.


1

Harici bir değişkene bir değer koymanın basit bir yolu (anonymus sınıfına ait değildir), işte budur!

Aynı şekilde, harici bir değişkenin değerini almak istiyorsanız, istediğinizi döndüren bir yöntem oluşturabilirsiniz!

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

Anonim sınıfların temelde lambdalar gibi olduğunu sanıyordum, ancak daha kötü sözdizimiyle ... bu doğru çıkıyor ama sözdizimi daha da kötü ve yerel değişkenlerin (ne olması gerektiği) kapsayıcı sınıfa sızmasına neden oluyor.

Hiçbir nihai değişkene, onları ana sınıfın alanları haline getirerek erişemezsiniz.

Örneğin

Arayüz:

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

sınıf:

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

Bunu java 8'de mi çözdüler bilmiyorum (EE dünyasında sıkışıp kaldım ve henüz 8'im yok) ama C #'da şöyle görünecek:

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

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

            typeProcessor.doStuffThatUsesLambda();
        }
    }

C # da ayrı bir arayüze ihtiyacınız yok ... Özledim! Kendimi java'da daha kötü tasarımlar yaparken buluyorum ve kendimi daha çok tekrar ediyorum çünkü bir şeyi yeniden kullanmak için java'ya eklemeniz gereken kod + karmaşıklık miktarı, çoğu zaman kopyalayıp yapıştırmaktan daha kötü.


Kullanabileceğiniz başka bir hack gibi görünüyor, burada bahsedildiği gibi stackoverflow.com/a/4732586/962696
JonnyRaa
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.