Являются ли статические анонимные классы определенно неправильными в Java? - PullRequest
4 голосов
/ 07 августа 2009

В другом месте я читал, что статический анонимный класс не имеет смысла - что все анонимные классы должны быть привязаны к экземпляру включающего типа. Но компилятор позволяет вам это сделать. Вот пример:

class Test {

    /*
     * What's the difference at between
     * Test.likeThis and Test.likeThat?        
     */

    // This is obviously okay:
    private static final class LikeThat {
        @Override
        public String toString() { return "hello!"; }       
    }
    public static Object likeThat = new LikeThat();

    // What about this - is it really any different? 
    public static Object likeThis = new Object() {
        @Override
        public String toString() { return "hello!"; }
    };
}

Что здесь происходит?

Ответы [ 9 ]

7 голосов
/ 07 августа 2009

С Спецификация языка Java, раздел 8.1.3 :

Экземпляр внутреннего класса I , объявление которого происходит в статическом контексте , не имеет лексически включающих экземпляров. Однако, если I немедленно объявлен в статическом методе или статическом инициализаторе, тогда у I есть включающий блок , который является самым внутренним оператором блока, лексически включающим объявление I .

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

Кстати, ваша формулировка немного обманчива, вы на самом деле имеете в виду статический экземпляр анонимного класса (это статический экземпляр, а не класс).

7 голосов
/ 07 августа 2009

Я не вижу ничего плохого в статических анонимных классах

5 голосов
/ 07 августа 2009

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

И, конечно же, всегда учитывайте тестируемость класса и можете ли вы предоставить двойной тест, если возникнет такая необходимость

1 голос
/ 27 марта 2013

Согласно этому ответу , который ссылается на JLS, анонимными классами являются никогда static, но при создании в "статическом контексте" у них нет "включающего экземпляра".

Тем не менее,

  • Они выдают ту же ошибку во время компиляции, если вы пытаетесь сослаться на Test.this ( на нестатическую переменную this нельзя ссылаться из статического контекста )
  • Во время выполнения единственным очевидным отличием между объектами Class (кроме имени) является то, что Test$1 является «анонимным классом», а Test$LikeThat - «членом-классом». У них обоих есть вмещающий класс; ни один из них не имеет вложенного конструктора или метода. (Я проверил только вероятностные методы; могут быть и другие различия.)
    • РЕДАКТИРОВАТЬ: Согласно getModifiers(), тест $ 1 равен static, а Test$LikeThat равен static final! Согласно спецификации языка, Test$1 должно быть final. Хм ...
  • Согласно javap -c -verbose -s -private -l,

    • Test$1 указывает «EnclosingMethod» (возможно, статический инициализатор Test?)
    • Test$LikeThat имеет дополнительную запись в "InnerClass" (#12; //class Test$1) и любопытный конструктор Test$LikeThat(Test$1). Похоже, это происходит потому, что LikeThat равен private, что делает конструктор private, поэтому компилятор генерирует «батут», чтобы он мог вызываться из Test.

Если вы удалите private, они, по-видимому, компилируются примерно в одно и то же, кроме записи EnclosingMethod.

Test$1 не имеет поля final Test this$0;, которое было бы, если бы оно было определено в нестатическом контексте.

1 голос
/ 07 августа 2009

Я делаю это все время. Это особенно удобно для специальных реализаций служебных интерфейсов, например ::1001

/** A holder for {@link Thing}s. */
public interface ThingsHolder {

    /** A {@link ThingsHolder} with nothing in it. */
    public static final ThingsHolder EMPTY_HOLDER = new ThingsHolder() {
        @Override
        public Iterable<Thing> getThings() {
            return Collections.emptySet();
        }
    };

    /** Provides some things. */
    Iterable<Thing> getThings();
}

Вы можете создать закрытый статический внутренний класс с именем EmptyHolder, и, возможно, в некоторых случаях это сделает код более читабельным, но нет никаких причин, по которым у вас есть , чтобы сделать это.

1 голос
/ 07 августа 2009

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

Широко распространенная идиома enum (до Java 5) использовала аналогичный подход с анонимными статическими наследниками класса enum. Возможно, сейчас лучше придерживаться Java 5 enum для этого случая.

Если вы можете найти подходящее реальное приложение для анонимных статических классов - почему бы не использовать их?

0 голосов
/ 06 октября 2009

Это может быть очень удобно из-за возможности делать круговые ссылки:

class A
{ 
    public static final A _1 = new A() {
        public A foo()
        {
            return _2;
        }
    };

    public static final A _2 = new A() {
        public A foo()
        {
            return _1;
        }
    };
}

Создание нескольких объектов, содержащих ссылки друг на друга, может быть очень неудобным без использования анонимных классов.

0 голосов
/ 07 августа 2009

Конечно, нет. Я всегда использую статические вложенные классы, если только мне не требуется неявная ассоциация с включающим объектом.

В терминологии Java вложенный класс: = класс, который объявлен в другом классе (или интерфейсе). Внутренние классы - это те вложенные классы, которые имеют связанный экземпляр из включающего класса. (Нестатические классы-члены, локальные классы, анонимные классы).

Неявная ассоциация может иногда предотвращать сборку мусора.

0 голосов
/ 07 августа 2009

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

Ну, кроме того, что это скрытый одноэлементный объект, это довольно зло.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...