在Java中的方法中使用类定义

时间:2010-03-11 19:55:01

标签: java class local-class

示例:

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. 我很难以面向对象的方式想象这个概念。在行为中包含类定义。也许有人可以告诉我相同的现实世界的例子。
  5. 方法中的抽象类对我来说听起来有点疯狂。但是不允许接口。这背后有什么理由吗?

7 个答案:

答案 0 :(得分:66)

这称为本地课程。

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

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

答案 1 :(得分:15)

这些被称为本地类。您可以找到详细说明和示例here。该示例返回一个特定的实现,我们不需要知道该方法之外的内容。

答案 2 :(得分:8)

  1. 从方法外部无法看到类(即实例化,其方法无反射访问)。此外,它可以访问testMethod()中定义的局部变量,但是在类定义之前。

  2. 我实际上想:“不会写这样的文件。”直到我试过它:哦,是的,这样的文件被创建了!它将被称为A $ 1B.class,其中A是外部类,B是本地类。

  3. 特别是对于回调函数(GUI中的事件处理程序,如单击Button时的onClick()等),通常使用“匿名类” - 首先是因为你最终可能会遇到很多他们但有时候匿名类不够好 - 尤其是你不能在它们上定义构造函数。在这些情况下,这些方法本地类可以是一个很好的选择。

答案 3 :(得分:7)

这个的真正目的是允许我们在函数调用中内联创建类来控制我们这些喜欢假装我们用函数式语言编写的人;)

答案 4 :(得分:3)

当你希望拥有一个完整的函数内部类和匿名类(a.k.a. Java封闭)时,唯一的情况是满足以下条件

  1. 您需要提供接口或抽象类实现
  2. 您想使用调用函数
  3. 中定义的一些最终参数
  4. 您需要记录接口调用的一些执行状态。
  5. E.g。有人想要一个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 );
    }
    

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

答案 5 :(得分:2)

定义内部类(在方法或类中)的主要原因是处理成员的可访问性以及封闭类和方法的变量。 内部类可以查找私有数据成员并对其进行操作。如果在方法中它也可以处理最终的局部变量。

拥有内部类有助于确保外部世界无法访问此类。这尤其适用于GWT或GXT等UI编程的情况,其中JS生成代码是用java编写的,每个按钮或事件的行为都必须通过创建匿名类来定义

答案 6 :(得分: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());
        }
    }