在 Java 方法中使用类定义

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2428186/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 07:29:20  来源:igfitidea点击:

Use of class definitions inside a method in Java

javaclasslocal-class

提问by bragboy

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

I found out that the above piece of code is perfectly legal in Java. I have the following questions.

我发现上面这段代码在 Java 中是完全合法的。我有以下问题。

  1. What is the use of ever having a class definition inside a method?
  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. Abstract classes inside a method sounds a bit crazy to me. But no interfaces allowed. Is there any reason behind this?
  1. 在方法中定义类有什么用?
  2. 是否会生成一个类文件 DummyClass
  3. 我很难以面向对象的方式想象这个概念。在行为中有一个类定义。可能有人可以告诉我等效的现实世界示例。
  4. 方法中的抽象类对我来说听起来有点疯狂。但不允许接口。这背后有什么原因吗?

采纳答案by Jacob Mattison

This is called a local class.

这称为局部类。

2 is the easy one: yes, a class file will be generated.

2 是最简单的:是的,将生成一个类文件。

1 and 3 are kind of the same question. You would use a local class where you never need to instantiate one or know about implementation details anywhere but in one method.

1和3是同一个问题。您将使用一个本地类,您永远不需要在任何地方实例化一个类或了解实现细节,但在一种方法中。

A typical use would be to create a throw-away implementation of some interface. For example you'll often see something like this:

一个典型的用途是创建一些接口的一次性实现。例如,你会经常看到这样的事情:

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

If you needed to create a bunch of these and do something with them, you might change this to

如果您需要创建一堆这些并使用它们做一些事情,您可以将其更改为

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

Regarding interfaces: I'm not sure if there's a technical issue that makes locally-defined interfaces a problem for the compiler, but even if there isn't, they wouldn't add any value. If a local class that implements a local interface were used outside the method, the interface would be meaningless. And if a local class was only going to be used inside the method, both the interface and the class would be implemented within that method, so the interface definition would be redundant.

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

回答by Chris Lercher

  1. The class can't be seen (i.e. instantiated, its methods accessed without Reflection) from outside the method. Also, it can access the local variables defined in testMethod(), but before the class definition.

  2. I actually thought: "No such file will be written." until I just tried it: Oh yes, such a file is created! It will be called something like A$1B.class, where A is the outer class, and B is the local class.

  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.

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

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

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

回答by BalusC

Those are called local classes. You can find a detailed explanation and an example here. The example returns a specific implementation which we doesn't need to know about outside the method.

那些被称为本地类。您可以在此处找到详细说明和示例。该示例返回一个特定的实现,我们不需要在方法之外了解它。

回答by Steve B.

The real purpose of this is to allow us to create classes inline in function calls to console those of us who like to pretend that we're writing in a functional language ;)

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

回答by Fazal

The main reason to define inner classes (within a method or a class) is to deal with accessibility of members and variables of the enclosing class and method. An inner class can look up private data members and operate on them. If within a method it can deal with final local variable as well.

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

Having inner classes does help in making sure this class is not accessible to outside world. This holds true especially for cases of UI programming in GWT or GXT etc where JS generating code is written in java and behavior for each button or event has to be defined by creating anonymous classes

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

回答by Alexander Pogrebnyak

The only case when you would like to have a full blown function inner class vs anonymous class ( a.k.a. Java closure ) is when the following conditions are met

当您希望拥有完整的函数内部类与匿名类(又名 Java 闭包)的唯一情况是满足以下条件时

  1. you need to supply an interface or abstract class implementation
  2. you want to use some final parameters defined in calling function
  3. you need to record some state of execution of the interface call.
  1. 您需要提供接口或抽象类实现
  2. 您想使用调用函数中定义的一些最终参数
  3. 你需要记录一些接口调用的执行状态。

E.g. somebody wants a Runnableand you want to record when the execution has started and ended.

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

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

使用匿名类是不可能的,使用内部类可以做到这一点。

Here is an example do demonstrate my point

这是一个例子来证明我的观点

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

Before using this pattern though, please evaluate if plain old top-level class, or inner class, or static inner class are better alternatives.

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

回答by s-evgheni

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.

我在 Spring 中遇到了一个很好的例子。该框架在方法内部使用本地类定义的概念以统一的方式处理各种数据库操作。

Assume you have a code like this:

假设你有这样的代码:

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

Let's first look at the implementation of the execute():

我们先来看看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());
    }

Please note the last line. Spring does this exact "trick" for the rest of the methods as well:

请注意最后一行。Spring 也为其余方法执行了这个精确的“技巧”:

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

The "trick" with local classes allows the framework to deal with all of those scenarios in a single method which accept those classes via StatementCallback interface. This single method acts as a bridge between actions (execute, update) and common operations around them (e.g execution, connection management, error translation and dbms console output)

本地类的“技巧”允许框架在通过 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());
        }
    }