116

例:

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. メソッド内の抽象クラスは、私には少しおかしなことに聞こえます。ただし、インターフェースは許可されていません。これには何か理由がありますか?
4

7 に答える 7

78

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

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

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

于 2010-03-11T20:15:17.417 に答える
17

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

于 2010-03-11T20:02:32.737 に答える
11
  1. クラスは、メソッドの外部からは見ることができません(つまり、インスタンス化され、そのメソッドはReflectionなしでアクセスされます)。また、testMethod()で定義されているローカル変数にアクセスできますが、クラス定義の前にアクセスできます。

  2. 私は実際に「そのようなファイルは書き込まれない」と思いました。試してみるまで:そうそう、そのようなファイルが作成されます!これは、A $ 1B.classのようなものと呼ばれます。ここで、Aは外部クラス、Bはローカルクラスです。

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

于 2010-03-11T20:00:56.877 に答える
7

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

于 2010-03-11T20:07:45.370 に答える
4

本格的な関数の内部クラスと匿名クラス(別名Javaクロージャ)が必要な場合は、次の条件が満たされている場合のみです。

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

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

于 2010-03-11T20:45:50.670 に答える
2

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

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

于 2010-03-11T20:27:33.747 に答える
2

私は春に良い例に出くわしました。フレームワークは、メソッド内のローカルクラス定義の概念を使用して、さまざまなデータベース操作を統一された方法で処理しています。

次のようなコードがあるとします。

JdbcTemplate jdbcOperations = new JdbcTemplate(this.myDataSource);
jdbcOperations.execute("call my_stored_procedure()")
jdbcOperations.query(queryToRun, new MyCustomRowMapper(), withInputParams);
jdbcOperations.update(queryToRun, withInputParams);

まず、execute()の実装を見てみましょう。

    @Override
    public void execute(final String sql) throws DataAccessException {
        if (logger.isDebugEnabled()) {
            logger.debug("Executing SQL statement [" + sql + "]");
        }

        /**
         * Callback to execute the statement.
         (can access method local state like sql input parameter)
         */
        class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
            @Override
            @Nullable
            public Object doInStatement(Statement stmt) throws SQLException {
                stmt.execute(sql);
                return null;
            }
            @Override
            public String getSql() {
                return sql;
            }
        }

        //transforms method input into a functional Object
        execute(new ExecuteStatementCallback());
    }

最後の行に注意してください。Springは、残りのメソッドでもこの正確な「トリック」を実行します。

//uses local class QueryStatementCallback implements StatementCallback<T>, SqlProvider
jdbcOperations.query(...) 
//uses local class UpdateStatementCallback implements StatementCallback<Integer>, SqlProvider
jdbcOperations.update(...)

ローカルクラスの「トリック」により、フレームワークは、StatementCallbackインターフェイスを介してこれらのクラスを受け入れる単一のメソッドでこれらすべてのシナリオを処理できます。この単一のメソッドは、アクション(実行、更新)とそれらを取り巻く一般的な操作(実行、接続管理、エラー変換、dbmsコンソール出力など)の間のブリッジとして機能します。

public <T> T execute(StatementCallback<T> action) throws DataAccessException    {
        Assert.notNull(action, "Callback object must not be null");

        Connection con = DataSourceUtils.getConnection(obtainDataSource());
        Statement stmt = null;
        try {
            stmt = con.createStatement();
            applyStatementSettings(stmt);
            //
            T result = action.doInStatement(stmt);
            handleWarnings(stmt);
            return result;
        }
        catch (SQLException ex) {
            // Release Connection early, to avoid potential connection pool deadlock
            // in the case when the exception translator hasn't been initialized yet.
            String sql = getSql(action);
            JdbcUtils.closeStatement(stmt);
            stmt = null;
            DataSourceUtils.releaseConnection(con, getDataSource());
            con = null;
            throw translateException("StatementCallback", sql, ex);
        }
        finally {
            JdbcUtils.closeStatement(stmt);
            DataSourceUtils.releaseConnection(con, getDataSource());
        }
    }
于 2020-04-09T17:09:01.273 に答える