Подтвердить что ты не робот

Статическая инициализация в интерфейсе

Когда я попытался написать что-то вроде этого:

public interface MyInterface {
    static {
        System.out.println("Hello!");
    }
}

компилятор не смог его скомпилировать.

Но когда я написал что-то вроде этого:

interface MyInterface {
    Integer iconst = Integer.valueOf(1);
}

и декомпилировал его, я увидел статическую инициализацию:

public interface MyInterface{
    public static final java.lang.Integer i;

    static {};
      Code:
      0:   iconst_1
      1:   invokestatic    #1; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      4:   putstatic       #2; //Field i:Ljava/lang/Integer;
      7:   return
}

Не могли бы вы объяснить мне это поведение?

4b9b3361

Ответ 1

У вас может быть статическая инициализация, но у вас не может быть статический блок. Тот факт, что статическая инициализация нуждается в статическом блоке кода для реализации, изменяет синтаксис Java.

Суть в том, что вы не должны иметь код в интерфейсе (до Java 8), но вам разрешено инициализировать поля.

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

Ответ 2

Интерфейсы не должны иметь побочных эффектов, и это относится даже к статическим интинераторам. У них было бы очень зависящее от JVM поведение. Посмотрите на следующий код

public class InterfaceSideEffects {
  public static void main(String[] args) {
    System.out.println("InterfaceSideEffects.main()");
    Impl i=new Impl();
    System.out.println("Impl initialized");
    i.bla();
    System.out.println("Impl instance method invoked");
    Foo f=new Impl();
    System.out.println("Impl initialized and assigned to Foo");
    f.bla();
    System.out.println("Foo interface method invoked");
  }
}
interface Foo {
  int dummy=Bar.haveSideEffect();
  void bla();
}
class Bar {
  static int haveSideEffect() {
    System.out.println("interface Foo initialized");
    return 0;
  }
}
class Impl implements Foo {
  public void bla() {
  }
}

Как вы думаете, когда будет напечатано interface Foo initialized? Попробуйте угадать и запустить код впоследствии. Ответ может вас удивить.

Ответ 3

Вы можете обойти эту проблему - если вы рассматриваете ее как проблему - добавив второй непубличный класс в один и тот же файл.

public interface ITest {
  public static final String hello = Hello.hello();
}

// You can have non-public classes in the same file.
class Hello {
  static {
    System.out.println("Static Hello");
  }
  public static String hello() {
    System.out.println("Hello again");
    return "Hello";
  }
}

Тестирование:

public class Test {
  public void test() {
    System.out.println("Test Hello");
    System.out.println(ITest.hello);
  }

  public static void main(String args[]) {
    try {
      new Test().test();
    } catch (Throwable t) {
      t.printStackTrace(System.err);
    }
  }

}

печатает:

Test Hello
Static Hello
Hello again
Hello

Java - такой умный язык - это затрудняет делать глупые вещи, но не невозможно.:)

Ответ 4

Интегралы не имеют никаких блоков инициализации. Следующий фрагмент кода может быть полезен.

public interface MyInterface {
public static final int a;// Compilation error as there is no way for 
                          // explicit initialization

}

public class MyClass {
public static final int a;// Still no error as there is another way to 
                          //initialize variable even though they are final.
 static{
    a=10;
   }

}

Ответ 5

Никогда не бывает смысла объявлять статический метод в интерфейсе. Они не могут быть выполнены обычным вызовом MyInterface.staticMethod(). (EDIT: Поскольку это последнее предложение смутило некоторых людей, вызов MyClass.staticMethod() выполняет точно реализацию staticMethod на MyClass, который, если MyClass - это интерфейс, не может существовать!) Если вы вызываете их, указав класс реализации MyImplementor.staticMethod() то вы должны знать фактический класс, поэтому не имеет значения, содержит ли он этот интерфейс или нет.

Что еще более важно, статические методы никогда не переопределяются, и если вы попытаетесь сделать:

MyInterface var = new MyImplementingClass();
var.staticMethod();

правила для static говорят, что метод, определенный в объявленном типе var, должен быть выполнен. Поскольку это интерфейс, это невозможно.

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

Чтобы ответить на некоторые из приведенных ниже комментариев, причина, по которой вы не можете выполнить "result = MyInterface.staticMethod()", заключается в том, что она должна была выполнить версию метода, определенного в MyInterface. Но не может быть версии, определенной в MyInterface, потому что это интерфейс. Он не имеет кода по определению.