匿名クラスにパラメーターを渡す方法は?


146

匿名クラスにパラメーターを渡したり、外部パラメーターにアクセスしたりすることは可能ですか?例えば:

int myVariable = 1;

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

実際の名前付きクラスとしてリスナーを作成せずに、リスナーがmyVariableにアクセスしたり、myVariableを渡したりする方法はありますか?


7
final囲んでいるメソッドからローカル変数を参照できます。
トムホーティン-タックライン

私は、プライベートmyVariableインスタンスを初期化し、returnが原因で右中括弧で呼び出すことができるプライベートメソッドを定義するというAdam Mmlodzinskiの提案の外観が気に入っていますthis
dlamblin 2013

この質問は、いくつかの共通の目標があります:stackoverflow.com/questions/362424/...を
アラステア・マコーマック

匿名クラス内からグローバルクラス変数を使用することもできます。多分あまりきれいではないかもしれませんが、それは仕事をすることができます。
ジョリ2013

回答:


78

匿名クラスはコンストラクタを持つことができないため、厳密にはいいえ。

ただし、クラスはスコープを含む変数を参照できます。匿名クラスの場合、これらは、含まれているクラスのインスタンス変数、またはfinalとマークされているローカル変数です。

edit:Peterが指摘したように、匿名クラスのスーパークラスのコンストラクターにパラメーターを渡すこともできます。


21
匿名クラスは、その親のコンストラクターを使用します。例new ArrayList(10) { }
Peter Lawrey、

いい視点ね。そのため、パラメーターを匿名クラスに渡すもう1つの方法ですが、そのパラメーターを制御できない可能性があります。
マシューウィリス

匿名クラスはコンストラクターを必要としません
newacct '28

4
匿名クラスには、インスタンス初期化子を含めることができます。これは、匿名クラスのパラメーターなしのコンストラクターとして機能できます。これらは、フィールド割り当てと同じ順序で実行されます。つまりsuper()、実際のコンストラクタの残りの部分の前後に実行されます。new someclass(){ fields; {initializer} fields; methods(){} }。静的イニシャライザのようなものですが、staticキーワードがありません。docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.6
マーク

このstackoverflow.com/a/3045185/1737819を参照してください。コンストラクタなしで実装する方法が示されています。
開発者MariusŽilėnas15年

336

はい、「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ですか?構文がわかりません。
ジョリ2013

11
myButton.addActionListener(...)は、そのメソッドを呼び出すと返されるオブジェクトとしてActionListenerオブジェクトを想定しているためです。

1
たぶん..それはうまくいくけれども、私自身はむしろ醜いことに気づきます。ほとんどの場合、必要な変数と関数パラメーターを最終的なものにして、内部クラスから直接参照するだけの余裕があることがほとんどです。
トーマス

2
よりシンプル:private int anonVar = myVariable;
Anm

29

はい。内部クラスから見える変数をキャプチャできます。唯一の制限は、それが最終でなければならないことです


匿名クラスから参照されるインスタンス変数は、最終的なafaikである必要はありません。
マシューウィリス

8
インスタンス変数thisは、最終的にwhichを介して参照されます。
Peter Lawrey

変数をに変更したくない場合はどうなりfinalますか?他に選択肢はありません。これは、になるように設計されているoriginパラメータに影響を与える可能性がありますfinal
Alston、2014

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に示すように、インスタンス初期化子を追加できます。これは名前がなく、最初に実行されるブロックです(コンストラクターのように)。

それらは、なぜJavaインスタンス初期化子で議論されているようですか?およびインスタンス初期化子はコンストラクターどのように異なりますか?コンストラクタとの違いについて説明します。


これは、尋ねられている質問を解決しません。ローカル変数へのアクセスの問題は依然として残るため、Adam Mlodzinskiまたはadarshrのソリューションを使用する必要があります
Matt Klein

1
@MattKlein私には、それはそれを解決するように見えます。それは実際には同じことであり、冗長ではありません。
haelix 2013

1
質問では、パラメーターを必要とするコンストラクターの場合と同様に、クラスにパラメーターを渡す方法を知りたがっていました。リンク(ここでは要約されているはずです)は、パラメーターなしのインスタンス初期化子を設定する方法を示しているだけで、質問には答えていません。この手法はfinalaavで説明されている変数で使用できますが、その情報はこの回答では提供されていません。断然、最良の答えは、Adam Mlodzinksiによって与えられた答えです(私は現在、このパターンを排他的に使用しており、これ以上決勝戦はありません!)。私はこれが尋ねられた質問に答えないという私のコメントを待ちます。
マットクライン

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

この例では、新しい無名クラスメソッドは次のように参照されます。

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

これは良いことです。匿名クラスをいくつかの識別しやすい変数に制限し、いくつかの変数をfinalにしなければならないという嫌悪感を取り除きます。
2013

3

他の人々はすでに、匿名クラスは最終的な変数にしかアクセスできないと答えています。しかし、元の変数を最終的でないものに保つ方法については未解決のままにしておきます。Adam Mlodzinskiが解決策を提供しましたが、かなり肥大化しています。この問題にはもっと簡単な解決策があります:

myVariablefinalになりたくない場合は、それが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は彼の答えでは何もせず、はるかに多くのコードを使用しています。


これはまだ余分なスコープなしで動作します。finalを使用した他の回答と実質的に同じです。
Adam Mlodzinski 2013年

@AdamMlodzinskiいいえ、プライベートスコープに元の変数の値を持つ新しい変数が導入されるため、回答と実質的に同じです。
2013年

事実上同じではありません。あなたの場合、内部クラスはanonVarを変更できません-したがって、効果は異なります。たとえば、内部クラスで状態を維持する必要がある場合、コードでは、プリミティブではなくセッター付きのある種のオブジェクトを使用する必要があります。
Adam Mlodzinski 2013年

@AdamMlodzinskiそれは問題ではありませんでした。問題は、それ自体を最終的にせずに外部変数にアクセスする方法でした。そして解決策は、最終的なコピーを作成することです。そしてもちろん、リスナーで変数の追加の可変コピーを作成できることは明らかです。しかし、最初はそれは尋ねられませんでした、そして、それはどんなinit方法も必要としません。この変数を追加するために、コードを1行追加することができます。ビルダーパターンの大ファンである場合は、自由に使用できますが、この場合は必要ありません。
2013年

これがfinal変数ソリューションを使用する場合とどのように異なるかはわかりません。
Kevin Rave、

3

単純なラムダを使用できます(「ラムダ式は変数をキャプチャできます」)

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

(anonymusクラスに属していない)外部変数に値を入力する簡単な方法は、次のとおりです。

同様に、外部変数の値を取得する場合は、必要なものを返すメソッドを作成できます。

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

私は匿名クラスは基本的にラムダのようだと思っていましたが、構文が悪いです...これは真実であることが判明しましたが、構文はさらに悪く、ローカル変数が含まれているクラスにローカル変数を流出させます。

親クラスのフィールドにすることで、最終的な変数にアクセスすることはできません。

例えば

インターフェース:

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#でも別のインターフェースを必要としません...私はそれが恋しいです!何かを再利用するためにJavaに追加しなければならないコードの量と複雑さは、多くの場合、単にコピーして貼り付けるよりも悪いので、私はJavaでより悪いデザインを作り、自分自身を繰り返します。


あなたが使用できる別のハックのように見え、ここで述べたように1つの要素の配列を持つことであるstackoverflow.com/a/4732586/962696
JonnyRaa
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.