Відповіді:
Так, ви можете створити як вкладений клас, так і внутрішній клас всередині інтерфейсу Java (зауважте, що, на відміну від поширеної думки, не існує такого поняття як " статичний внутрішній клас ": це просто не має сенсу, немає нічого "внутрішнього" та " outter "клас, коли вкладений клас є статичним, тому він не може бути" статичним внутрішнім ").
У будь-якому випадку, наступне компілюється чудово:
public interface A {
class B {
}
}
Я бачив, як раніше застосовували якусь «перевірку контрактів» безпосередньо у визначенні інтерфейсу (ну, у класі, вкладеному в інтерфейс, який може мати статичні методи, на відміну від самого інтерфейсу, що не може). Виглядаючи так, якщо я правильно згадую.
public interface A {
static class B {
public static boolean verifyState( A a ) {
return (true if object implementing class A looks to be in a valid state)
}
}
}
Зауважте, що я не коментую корисність такої речі, я просто відповідаю на ваше запитання: це можна зробити, і це один із видів використання, який я вже бачив із цього.
Тепер я не буду коментувати корисність такої конструкції, і з того, що я бачив: я бачив це, але це не дуже поширена конструкція.
Кодова база 200KLOC тут, де це трапляється рівно нульовий раз (але тоді ми маємо багато інших речей, які ми вважаємо поганими практиками, які трапляються рівно нульовим часом, що інші люди вважали б цілком нормальними, тому ...).
interface
s не може мати внутрішні класи. Ви можете опустити static
модифікатор interface
вкладеного класу, але все-таки це вкладений клас, а не внутрішній клас.
B
клас є статичним вкладеним класом, а не внутрішнім класом; інтерфейси отримують особливу обробку. Я не зміг знайти згадки про це в Інтернеті, за винятком самої специфікації: "Клас-член інтерфейсу є неявно статичним, тому ніколи не вважається внутрішнім класом". docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3
Так, ми можемо мати класи всередині інтерфейсів. Одним із прикладів використання може бути
public interface Input
{
public static class KeyEvent {
public static final int KEY_DOWN = 0;
public static final int KEY_UP = 1;
public int type;
public int keyCode;
public char keyChar;
}
public static class TouchEvent {
public static final int TOUCH_DOWN = 0;
public static final int TOUCH_UP = 1;
public static final int TOUCH_DRAGGED = 2;
public int type;
public int x, y;
public int pointer;
}
public boolean isKeyPressed(int keyCode);
public boolean isTouchDown(int pointer);
public int getTouchX(int pointer);
public int getTouchY(int pointer);
public float getAccelX();
public float getAccelY();
public float getAccelZ();
public List<KeyEvent> getKeyEvents();
public List<TouchEvent> getTouchEvents();
}
Тут код має два вкладені класи, які призначені для інкапсуляції інформації про об'єкти подій, які згодом використовуються у визначеннях методів, таких як getKeyEvents (). Присутність їх у вхідному інтерфейсі покращує згуртованість.
Дійсне використання IMHO - це визначення об'єктів, які отримуються або повертаються методами інтерфейсу, що додається. Типово структури зберігання даних. Таким чином, якщо об'єкт використовується тільки для цього інтерфейсу, у вас є речі більш згуртовані.
На прикладі:
interface UserChecker {
Ticket validateUser(Credentials credentials);
class Credentials {
// user and password
}
class Ticket {
// some obscure implementation
}
}
Але як би там не було ... це лише справа смаку.
Цитата з специфікації Java 7 :
Інтерфейси можуть містити декларації типу членів (§8.5).
Оголошення типу учасника в інтерфейсі є неявно статичним і загальнодоступним. Дозволено надмірно вказати один або обидва ці модифікатори.
НЕ можна оголошувати нестатичні класи всередині інтерфейсу Java, що для мене є сенсом.
Цікавим випадком використання є надання типу реалізації за замовчуванням для інтерфейсу методів через внутрішній клас, як описано тут: https://stackoverflow.com/a/3442218/454667 (для подолання проблеми успадкування одного класу).
Це, безумовно, можливо, і один випадок, коли мені здається корисним, це коли інтерфейс повинен викидати власні винятки. Ви зберігаєте винятки із пов’язаним із ними інтерфейсом, який, на мою думку, часто акуратніше, ніж засмічення вашого вихідного дерева купою дрібницьких файлів виключень.
interface MyInterface {
public static class MyInterfaceException extends Exception {
}
void doSomething() throws MyInterfaceException;
}
Так, можна мати визначення статичних класів всередині інтерфейсу, але, мабуть, найбільш корисним аспектом цієї функції є використання типів перечислення (які є особливим видом статичних класів). Наприклад, ви можете мати щось подібне:
public interface User {
public enum Role {
ADMIN("administrator"),
EDITOR("editor"),
VANILLA("regular user");
private String description;
private Role(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
public String getName();
public void setName(String name);
public Role getRole();
public void setRole(Role role);
...
}
Те, що згадує @Bachi, схоже на риси в Scala і насправді реалізується за допомогою вкладеного класу всередині інтерфейсу. Це можна змоделювати на Java. Дивіться також риси Java або шаблон мікшинів?
Можливо, коли вам потрібні більш складні конструкції, такі як різні способи реалізації, розгляньте:
public interface A {
public void foo();
public static class B implements A {
@Override
public void foo() {
System.out.println("B foo");
}
}
}
Це ваш інтерфейс, і це буде реалізованим:
public class C implements A {
@Override
public void foo() {
A.B b = new A.B();
b.foo();
}
public static void main(String[] strings) {
C c = new C();
c.foo();
}
}
Може надати деякі статичні реалізації, але чи не буде це бентежити, я не знаю.
Я знайшов використання ялиці цього типу конструкції.
Ви маєте доступ до всіх згрупованих констант; ім'я класу в цьому випадку виконує роль простору імен.
Зараз мені це потрібно. У мене є інтерфейс, де було б зручно повернути унікальний клас з декількох його методів. Цей клас має сенс лише як контейнер для відповідей методів цього інтерфейсу.
Отже, було б зручно мати статичне визначення вкладеного класу, яке пов'язане лише з цим інтерфейсом, оскільки цей інтерфейс повинен бути єдиним місцем, де коли-небудь створюється цей клас контейнера результатів.