Внутренние классы-члены (member inner slasses)

Внутренние классы в Java делятся на такие три вида:

· внутренние классы-члены (member inner classes);

· локальные классы (local classes);

· анонимные классы (anonymous classes).

Внутренние классы-члены ассоциируются не с самим внешним классом, а с его экземпляром. При этом они имеют доступ ко всем его полям и методам. Например:

public class Users {
...
public class Query {
private Query() { ... }
public void setLogin(String login) { ... }
public void setCreationDate(Date date) { ... }
public List<User> list() { ... }
public User single() { ... }
}

public Query createQuery() { return new Query(); }
}

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

Users.Query query = users.createQuery();
query.setCreationDate(date);
List<User> users = query.list();

Если бы конструктор класса Query был объявлен как public, создать экземпляр класса Query снаружи можно было бы только через инстанс обрамляющего класса:

Users users = new Users();
Users.Query query = users.new Query();

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

Еще одна важная особенность - интерфейсы в Java не могут быть инстанциированы, соответственно объявить нестатический внутренний интерфейс нельзя, так как элементарно не будет объекта для ассоциации с экземпляром внешнего класса. Объявление вида:

public class OuterClass {
public interface ImNonStaticInterface { ... }
}

на самом деле будет интерпретироваться так:

public class OuterClass {

public static interface ImNonStaticInterface { ... }

}

то есть неявно будет добавлен модификатор static.

Как было сказано выше, member inner class имеет доступ ко всем полям и методам обрамляющего класса. Давайте рассмотрим такой фрагмент кода:

public class OuterClass {
public void method() { ... }

public class InnerClass {
public void method() { ... }

public void anotherMethod() {
method();
}
}
}

Вызов method() из anotherMethod обратится к методу класса InnerClass. Для обращения к методу обрамляющего класса необходимо использовать такую конструкцию - OuterClass.this.method().

Локальные классы

Локальные классы (local classes) определяются в блоке Java кода. На практике чаще всего объявление происходит в методе некоторого другого класса. Хотя объявлять локальный класс можно внутри статических и нестатических блоков инициализации.

Пример использования локального класса:

public class Handler {
public void handle(String requestPath) {
class Path {
List<String> parts = new ArrayList<String>();
String path = "/";
Path(String path) {
if (path == null) return;
this.path = path;
for (String s : path.split("/"))
if (s.trim().length() > 0) this.parts.add(s);
}
int size() { return parts.size(); }
String get(int i) { return i > this.parts.size() - 1 ? null : this.parts.get(i); }
boolean startsWith(String s) { return path.startsWith(s); }
}

Path path = new Path(requestPath);

if (path.startsWith("/page")) {
String pageId = path.get(1);
...
}
if (path.startsWith("/post")) {
String categoryId = path.get(1);
String postId = path.get(2);
...
}
...
}
}

Данный код с некоторыми изменениями взят из реального проекта и используется для обработки get запросов к веб-серверу. Он вводит новую абстракцию, с которой удобно работать в пределах метода и которая не нужна за его пределами.

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

У локальных классов есть множество ограничений:

· они видны только в пределах блока, в котором объявлены;

· они не могут быть объявлены как private, public, protected или static;

· они не могут иметь внутри себя статических объявлений (полей, методов, классов); исключением являются константы (static final);

Кстати, интерфейсы тоже нельзя объявлять локально по тем же причинам, по каким их нельзя объявлять внутренними.

Наши рекомендации