在Java方法中使用类定义

wfypjpf4  于 2022-12-17  发布在  Java
关注(0)|答案(8)|浏览(97)

示例:

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.在一个方法中定义一个类有什么用呢?
1.是否将为DummyClass生成类文件
1.我很难想象这种面向对象的概念。在一个行为中有一个类定义。也许有人能用真实的世界中的例子来告诉我。
1.方法中的抽象类听起来有点疯狂。但是不允许接口。这背后有什么原因吗?

zpqajqem

zpqajqem1#

这称为局部类。
2是简单的一个:是的,将生成一个类文件。
1和3是同一个问题,你可以使用一个局部类,你不需要示例化一个局部类,也不需要知道实现细节,只需要在一个方法中。
一个典型的用法是创建一个接口的一次性实现,例如,你经常会看到这样的东西:

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

关于接口:我不确定是否有技术问题使得本地定义的接口成为编译器的问题,但即使没有,它们也不会添加任何值。如果实现本地接口的本地类在方法外部使用,则接口将毫无意义。如果本地类只在方法内部使用,则接口和类都将在该方法内部实现。因此接口定义是多余的。

rqqzpn5f

rqqzpn5f2#

这些被称为局部类。你可以找到详细的解释和一个here的例子。这个例子返回一个特定的实现,我们不需要知道方法之外的内容。

omvjsjqw

omvjsjqw3#

1.类在方法外部是不可见的(例如,示例化,不使用Reflection访问它的方法),而且,它可以访问testMethod()中定义的局部变量,但在类定义之前。
1.我真的以为:“不会写入这样的文件。”直到我刚刚试过:是的,创建了这样一个文件!它的名称类似于A$1B.class,其中A是外部类,B是本地类。
1.特别是对于回调函数(GUI中的事件处理程序,比如当按钮被点击时的onClick()等),使用“匿名类”是很常见的--首先是因为你可能会得到很多这样的类。但是有时匿名类并不够好--特别是你不能在它们上面定义构造函数。在这种情况下,这些方法本地类是一个很好的选择。

6mw9ycah

6mw9ycah4#

这样做的真实的目的是允许我们在函数调用中创建内联类,以安慰那些喜欢假装自己在用函数式语言编写代码的人;)

edqdpe6u

edqdpe6u5#

只有在满足以下条件时,您才希望拥有完整的函数内部类与匿名类(也称为Java闭包)
1.需要提供接口或抽象类实现
1.您希望使用在调用函数中定义的一些final参数
1.需要记录接口调用的执行状态。
例如,有人想要一个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 );
}

在使用此模式之前,请评估普通的旧顶级类、内部类还是静态内部类是更好的选择。

4bbkushb

4bbkushb6#

定义内部类(在一个方法或类中)的主要原因是处理封闭类和方法的成员和变量的可访问性。内部类可以查找私有数据成员并对其进行操作。如果在方法中,它也可以处理最终的局部变量。
拥有内部类确实有助于确保该类不能被外界访问,尤其是在GWT或GXT等的UI编程中,JS生成代码是用java编写的,每个按钮或事件的行为必须通过创建匿名类来定义

f4t66c6m

f4t66c6m7#

我在Spring中遇到过一个很好的例子,框架在方法内部使用局部类定义的概念,以统一的方式处理各种数据库操作。
假设您有这样的代码:

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

x6h2sr288#

这里的一切都很清楚,但是我想为下一个读者提供另一个合理用例的例子。
关于@jacob-mattison的回答,如果我们假设在接口的这些一次性实现中有一些共同的操作,那么,最好写一次,但也保持实现匿名:

//within some method
    abstract class myRunnableClass implements Runnable {

        protected abstract void DO_AN_SPECIFIC_JOB();

        public void run() {
            someCommonCode();
            //...
            DO_AN_SPECIFIC_JOB();
            //..
            anotherCommonCode();
        }
    }

然后,使用这个定义的类并单独实现特定任务就很容易了:

taskExecutor.execute(new myRunnableClass() {
        protected void DO_AN_SPECIFIC_JOB() {
            // Do something
        }
    });
    taskExecutor.execute(new myRunnableClass() {
        protected void DO_AN_SPECIFIC_JOB() {
            // Do another thing
        }
    });

相关问题