Есть ли такая вещь, как "локальный интерфейс" в Java?
Java позволяет мне определять локальные абстрактные классы, как в этом примере:
public class Foo {
public void foo() {
abstract class Bar { // Bar is a local class in foo() ...
abstract void bar();
}
new Bar() { // ... and can be anonymously instantiated
void bar() {
System.out.println("Bar!");
}
}.bar();
}
}
По какой-то причине, когда я пытаюсь определить "локальный интерфейс" вместо локального класса, вот так:
public class Foo {
public void foo() {
interface Bar { // Bar was supposed to be a local interface...
void bar();
}
new Bar() { // ... to be anonymously instantiated
void bar() {
System.out.println("Bar!");
}
}.bar();
}
}
Java жалуется, что "Панель интерфейса члена может быть определена только внутри класса или интерфейса верхнего уровня". Для этого есть причина? Или я пропустил ошибку, которую сделал?
4 ответа
Там просто нет определения для этого в JLS. Это просто не существует.
Что касается слабой причины, в соответствии с JLS 14.3:
Все локальные классы являются внутренними классами (§8.1.3).
Интерфейс не может быть внутренним ( JLS 8.1.3):
Интерфейсы-члены (§8.5) неявно статичны, поэтому они никогда не считаются внутренними классами.
Поэтому у нас не может быть локального интерфейса.
Это, я думаю, в дополнение к тому, что @SotiriosDelimanolis обнаружил, что InterfaceDeclaration не является BlockStatement.
Спецификация языка Java не говорит вам, почему она была разработана так, как она была, но она описывает, что есть, а что нет.
Тело метода имеет следующую форму
MethodBody:
Block
;
Block:
{ BlockStatementsopt }
BlockStatements:
BlockStatement
BlockStatements BlockStatement
BlockStatement:
LocalVariableDeclarationStatement
ClassDeclaration
Statement
Поэтому объявление класса разрешено, а интерфейс - нет.
Мы можем утверждать, что наличие локального интерфейса не очень полезно с точки зрения вызывающего. Это не служит какой-либо цели. Интерфейс предназначен для описания поведения, но поскольку интерфейс будет локальным, ни один вызывающий не сможет его использовать. Вы также можете определить и реализовать поведение в классе.
Локальные интерфейсы (и перечисления) были введены вместе с функцией классов записей:
- в JDK 15 в качестве функции предварительного просмотра (JEP 384) - см. https://openjdk.java.net/jeps/384
- в JDK 16 в качестве постоянной функции (JEP 395) - см. https://openjdk.java.net/jeps/395
К сожалению, эта функция немного скрыта в документации, но она работает.
Обе версии позволяют писать код, как показано ниже:
public class Main {
public int foo() {
interface Experimentable {
int bar();
}
Experimentable e = new Experimentable() {
@Override
public int bar() {
return 0;
}
};
return e.bar();
}
public static void main(String[] args) {
System.out.println(new Main().foo());
}
}
Java теперь (начиная с 16) поддерживает локальный интерфейс .
Локальный интерфейс - это вложенный интерфейс (§9 (Интерфейсы)), объявление которого немедленно содержится в блоке.
И ваш (немного поправил, потому что
bar
внедрение должно быть общедоступным) код компилируется нормально.
public class Foo {
public void foo() {
interface Bar { // Bar was supposed to be a local interface...
void bar();
}
new Bar() { // ... to be anonymously instantiated
public void bar() {
System.out.println("Bar!");
}
}.bar();
}
}
Компиляция:
~/tmp $ /usr/local/opt/java/bin/javac -version
javac 17
~/tmp $ /usr/local/opt/java/bin/javac Foo.java