在 Java 方法中使用类定义

Example:

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. Will a class file be generated for DummyClass
  3. It's hard for me to imagine this concept in an Object Oriented manner. Having a class definition inside a behavior. Probably can someone tell me with equivalent real world examples.
  4. 方法中的抽象类在我看来有点疯狂。但是不允许使用接口。这背后有什么原因吗?
89319 次浏览
  1. 该类不能从方法外部看到(例如,实例化,它的方法在没有反射的情况下被访问)。此外,它可以访问 testMethod ()中定义的本地变量,但是在类定义之前。

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

  3. Especially for callback functions (event handlers in GUIs, like onClick() when a Button is clicked etc.), it's quite usual to use "anonymous classes" - first of all because you can end up with a lot of them. But sometimes anonymous classes aren't good enough - especially, you can't define a constructor on them. In these cases, these method local classes can be a good alternative.

这叫 本地课程。您可以找到一个详细的解释和一个例子 here。该示例返回一个我们不需要在方法外部知道的特定实现。

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

这就是所谓的本地类。

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

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

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

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

希望函数内部类与匿名类(也称为 Java 闭包)完全匹配的唯一情况是满足以下条件

  1. 您需要提供一个接口或抽象类实现
  2. 您需要使用在调用函数中定义的一些最终参数
  3. 您需要记录接口调用的某种执行状态。

例如,有人想要一个 Runnable,你想要记录执行开始和结束的时间。

With anonymous class it is not possible to do, with inner class you can do this.

这里有一个例子来说明我的观点

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

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

I've came across a good example in the Spring. The framework is using concept of local class definitions inside of the method to deal with various database operations in a uniform way.

假设您有这样的代码:

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