Инициализировать статические переменные интерфейса через Enum

Я пытаюсь понять поведение ниже, все, что я пытаюсь, это инициализировать статическую переменную интерфейса с помощью метода перечисления.

enum Hello {
    ProfileResolver();

    public Hello resolve() {
        System.out.println("resolve method called!!!!");
        return ProfileResolver;
    }
}
public interface Resolver{
  Hello hello = Hello.ProfileResolver.resolve(); // this should called when creating an instace of any implementation
}

Класс внедрения, как показано ниже

public class Impl implements Resolver{
}

а теперь если

public static void main(String[] arg){
   Resolver resolver = new Impl();
}

Теперь в этот момент я ожидал, что непосредственно перед инициализацией объекта Impl переменная приветствия интерфейса должна инициализировать и разрешить вызванный метод, но это не так. И когда я объявляю Resolver как класс вместо интерфейса, он работает, как и ожидалось. Может ли кто-нибудь помочь мне попытаться понять?


person SBhogal    schedule 05.04.2021    source источник
comment
Переменная hello в вашем интерфейсе — это static и final. Эти модификаторы являются неявными для свойств в интерфейсе.   -  person ernest_k    schedule 05.04.2021
comment
@ernest_k, я знаю, что static и final неявны в интерфейсе, но я пытаюсь понять, когда и как инициализируется эта константа hello, насколько я понимаю, как только класс загружается, запускаются первые статические переменные и статические блоки, поэтому в этом случае когда я пытаюсь создать объект класса Impl, который реализует интерфейс Resolver. Так что не следует вызывать метод разрешения, поскольку он вызывается при инициализации приветствия до создания объекта Impl ??   -  person SBhogal    schedule 05.04.2021
comment
Поскольку это зависит от вашей среды выполнения, отметьте свой вопрос с помощью компилятора/IDE, который вы используете, а также фактической версии Java (если не Java 8)   -  person ernest_k    schedule 05.04.2021
comment
@Sebastian, я пытался отлаживать, но метод разрешения не вызывается, но, согласно вашему предложению, он пытался ссылаться на переменную hello в классе реализации, например `private Hello implHello = hello; и это сработало. Версия JVM: версия openjdk 1.8.0_282. Можете ли вы сказать мне, какую информацию мне здесь не хватает? и если это относится к JIT-оптимизации, разве это не уместно, поскольку влияет на код или логику конечного пользователя.   -  person SBhogal    schedule 05.04.2021


Ответы (1)


Вот указанные триггеры для статической инициализации типов в Java. Это из языка Java. Спецификация

12.4.1. Когда происходит инициализация
Класс или тип интерфейса T будет инициализирован непосредственно перед первым появлением любого из следующих событий:

  • T — это класс, и создается экземпляр T.
  • Вызывается статический метод, объявленный T.
  • Присваивается статическое поле, объявленное T.
  • Используется статическое поле, объявленное T, и это поле не является постоянной переменной (§4.12.4).

При инициализации класса инициализируются его суперклассы (если они не были инициализированы ранее), а также любые суперинтерфейсы (§8.1.5), которые объявляют любые методы по умолчанию (§9.4.3) (если они не были предварительно инициализированы). Инициализация интерфейса сама по себе не вызывает инициализацию какого-либо из его суперинтерфейсов.

Ссылка на статическое поле (§8.3.1.1) вызывает инициализацию только класса или интерфейса, который фактически объявляет его, даже если на него можно ссылаться через имя подкласса, субинтерфейса или класса, реализующего интерфейс.

Вызов определенных рефлексивных методов в классе Class и в пакете java.lang.reflect также вызывает инициализацию класса или интерфейса.

Класс или интерфейс не будут инициализированы ни при каких других обстоятельствах.

Тщательный анализ показывает, что ваш код не соответствует ни одному из требований для выполнения статической инициализации интерфейса Resolver...

  • new Impl() вызывает инициализацию Impl, но это не вызывает инициализацию Resolver, потому что Resolver не имеет методов по умолчанию (см. мой текст, выделенный полужирным курсивом).

  • Ничто не требует, чтобы ссылка на тип Resolver запускала инициализацию

  • Способ заставить этот код запускать инициализацию Resolver состоит в том, чтобы либо добавить к нему метод по умолчанию (в этом нет смысла), либо прочитать его статические переменные:

    public static void main(String[] args) throws Exception {
        Resolver resolver = new Impl();
        System.out.println(Resolver.hello); //causes initialization
    }
    

Если вы используете Hello hello = Hello.ProfileResolver.resolve(); для запуска некоторых предполагаемых побочных эффектов с помощью метода resolve(), то не делайте этого.

person ernest_k    schedule 05.04.2021
comment
Отличное объяснение, Эрнест, спасибо. - person jccampanero; 06.04.2021
comment
Спасибо!! ernest_k, это сработало, как только в интерфейсе был добавлен метод по умолчанию. - person SBhogal; 06.04.2021