web-dev-qa-db-ja.com

Javaのメソッド内でのクラス定義の使用

例:

public class TestClass {

    public static void main(String[] args) {
        TestClass t = new TestClass();
    }

    private static void testMethod() {
        abstract class TestMethod {
            int a;
            int b;
            int c;

            abstract void implementMe();
        }

        class DummyClass extends TestMethod {
            void implementMe() {}
        }

        DummyClass dummy = new DummyClass();
    }
}

上記のコードはJavaでは完全に合法であることがわかりました。以下の質問があります。

  1. メソッド内にクラス定義を持つことの使用は何ですか?
  2. DummyClassのクラスファイルが生成されますか
  3. この概念をオブジェクト指向で想像するのは難しいです。ビヘイビア内にクラス定義を持ちます。おそらく、誰かが同等の実世界の例で私に話すことができます。
  4. メソッド内の抽象クラスは、ちょっとおかしく聞こえます。ただし、インターフェイスは許可されていません。これには理由がありますか?
98
bragboy

これはローカルクラスと呼ばれます。

2は簡単です。はい、クラスファイルが生成されます。

1と3は同じ質問です。ローカルクラスを使用するのは、1つのメソッド以外の場所でインスタンス化する必要も、実装の詳細を知る必要もない場合です。

典型的な用途は、何らかのインターフェースの使い捨て実装を作成することです。たとえば、次のようなものが頻繁に表示されます。

  //within some method
  taskExecutor.execute( new Runnable() {
       public void run() {
            classWithMethodToFire.doSomething( parameter );
       }
  }); 

これらの束を作成してそれらを使用する必要がある場合は、これを

  //within some method
  class myFirstRunnableClass implements Runnable {
       public void run() {
            classWithMethodToFire.doSomething( parameter );
       }
  }
  class mySecondRunnableClass implements Runnable {
       public void run() {
            classWithMethodToFire.doSomethingElse( parameter );
       }
  }
  taskExecutor.execute(new myFirstRunnableClass());
  taskExecutor.execute(new mySecondRunnableClass());

インターフェースに関して:ローカル定義のインターフェースがコンパイラーにとって問題となる技術的な問題があるかどうかはわかりませんが、たとえ存在しなくても、付加価値はありません。ローカルインターフェイスを実装するローカルクラスがメソッドの外部で使用された場合、インターフェイスは無意味になります。また、ローカルクラスがメソッド内でのみ使用される場合、インターフェイスとクラスの両方がそのメソッド内で実装されるため、インターフェイス定義は冗長になります。

66
Jacob Mattison

それらはローカルクラスと呼ばれます。詳細な説明と例があります here 。この例は、メソッドの外部について知る必要のない特定の実装を返します。

15
BalusC
  1. クラスは、メソッドの外部からは見えません(つまり、インスタンス化され、そのメソッドはReflectionなしでアクセスされます)。また、testMethod()で定義されたローカル変数にアクセスできますが、クラス定義の前にアクセスできます。

  2. 私は実際に考えました:「そのようなファイルは書かれません」。試してみるまで:そうそう、そのようなファイルが作成されます! A $ 1B.classのような名前になります。ここで、Aは外部クラス、Bはローカルクラスです。

  3. 特に、コールバック関数(ボタンがクリックされたときのonClick()などのGUIのイベントハンドラー)の場​​合、「匿名クラス」を使用することは非常に一般的です。ただし、匿名クラスでは不十分な場合があります。特に、コンストラクターを定義できない場合があります。これらの場合、これらのメソッドローカルクラスが適切な代替手段となります。

9
Chris Lercher

これの本当の目的は、関数呼び出しでクラスをインラインで作成し、関数型言語で書いているふりをしたい人を慰めることです;)

7
Steve B.

完全な関数内部クラスと匿名クラス(a.k.a. Java closure)を使用したい場合は、次の条件が満たされている場合のみです。

  1. インターフェイスまたは抽象クラスの実装を提供する必要があります
  2. 関数の呼び出しで定義されたいくつかの最終パラメータを使用したい
  3. インターフェース呼び出しの実行状態を記録する必要があります。

例えば。誰かがRunnableを望んでいて、実行の開始と終了を記録したい場合。

匿名クラスでは実行できませんが、内部クラスではこれを実行できます。

ここに私のポイントを示す例があります

private static void testMethod (
        final Object param1,
        final Object param2
    )
{
    class RunnableWithStartAndEnd extends Runnable{
        Date start;
        Date end;

        public void run () {
            start = new Date( );
            try
            {
                evalParam1( param1 );
                evalParam2( param2 );
                ...
            }
            finally
            {
                end = new Date( );
            }
        }
    }

    final RunnableWithStartAndEnd runnable = new RunnableWithStartAndEnd( );

    final Thread thread = new Thread( runnable );
    thread.start( );
    thread.join( );

    System.out.println( runnable.start );
    System.out.println( runnable.end );
}

ただし、このパターンを使用する前に、単純な古いトップレベルクラス、内部クラス、または静的内部クラスがより良い代替手段であるかどうかを評価してください。

(メソッドまたはクラス内で)内部クラスを定義する主な理由は、メンバーと、それを囲むクラスとメソッドの変数のアクセシビリティに対処するためです。内部クラスは、プライベートデータメンバーを検索し、それらを操作できます。メソッド内であれば、最終ローカル変数も処理できます。

内部クラスを持つことは、このクラスが外部からアクセスできないようにするのに役立ちます。これは、JS生成コードがJavaで記述され、各ボタンまたはイベントの動作を匿名クラスを作成して定義する必要があるGWTやGXTなどのUIプログラミングの場合に特に当てはまります。

2
Fazal