Использование определений классов внутри метода в Java - PullRequest
97 голосов
/ 11 марта 2010

Пример:

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. Абстрактные классы внутри метода звучат для меня немного безумно. Но интерфейсы не допускаются. Есть ли причина этого?

Ответы [ 6 ]

66 голосов
/ 11 марта 2010

Это называется локальным классом.

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

Относительно интерфейсов: я не уверен, есть ли техническая проблема, которая делает локально определенные интерфейсы проблемой для компилятора, но даже если нет, они не добавят никакой ценности. Если бы локальный класс, который реализует локальный интерфейс, использовался вне метода, интерфейс был бы бессмысленным. И если локальный класс будет использоваться только внутри метода, и интерфейс, и класс будут реализованы в этом методе, поэтому определение интерфейса будет избыточным.

15 голосов
/ 11 марта 2010

Они называются локальные классы . Вы можете найти подробное объяснение и пример здесь . В примере возвращается конкретная реализация, о которой нам не нужно знать вне метода.

8 голосов
/ 11 марта 2010
  1. Класс не виден (т. Е. Создается экземпляр, доступ к его методам осуществляется без отражения) извне метода. Кроме того, он может обращаться к локальным переменным, определенным в testMethod (), но до определения класса.

  2. Я действительно думал: «Такой файл не будет записан». пока я просто не попробовал: о да, такой файл создан! Он будет называться чем-то вроде A $ 1B.class, где A - внешний класс, а B - локальный класс.

  3. Специально для функций обратного вызова (обработчики событий в графическом интерфейсе, например, onClick () при нажатии кнопки и т. Д.) Довольно часто используются «анонимные классы» - прежде всего потому, что вы можете получить много из них. Но иногда анонимные классы не достаточно хороши - особенно, вы не можете определить конструктор для них. В этих случаях эти локальные классы методов могут быть хорошей альтернативой.

7 голосов
/ 11 марта 2010

Настоящая цель этого состоит в том, чтобы позволить нам создавать встроенные классы в вызовах функций, чтобы утешить тех из нас, кто любит делать вид, что мы пишем на функциональном языке;)

3 голосов
/ 11 марта 2010

Единственный случай, когда вы хотите иметь полноценный внутренний класс функции против анонимного класса (например, закрытие 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 );
}

Прежде чем использовать этот шаблон, пожалуйста, оцените, являются ли простой старый класс верхнего уровня, или внутренний класс, или статический внутренний класс лучшими альтернативами.

2 голосов
/ 11 марта 2010

Основная причина определения внутренних классов (внутри метода или класса) заключается в том, чтобы иметь дело с доступностью членов и переменных окружающего класса и метода. Внутренний класс может искать личные данные членов и оперировать ими. Если внутри метода он также может иметь дело с конечной локальной переменной.

Наличие внутренних классов помогает убедиться, что этот класс не доступен для внешнего мира. Это особенно верно для случаев программирования пользовательского интерфейса в GWT или GXT и т. Д., Где код, генерирующий JS, написан на Java, и поведение для каждой кнопки или события должно быть определено путем создания анонимных классов

...