Inner classes in Java
In Java nested class - a class that is declared within another class declaration. Nested classes are divided into static and non-static . Strictly non-static nested classes have another name - inner classes.
Introduction
Let’s talk about terminology first. Major part of the literature on Java operates with such terms as inner classes and nested classes. Throughout this article we will be talking mainly about nested classes of which inner classes are a subset. Nevertheless frequently when talking about inner classes people mean all nested ones. Such a paradox.
Nested class - is a class declared inside another class’s declaration, like, for example:
class OuterClass {
...
static class StaticNestedClass {
...
}
...
}
All nested classes may be divided into static (StaticNestedClass in the above example) and non-static ones. Non-static nested classes are frequently referred to as simply inner classes. Regarding an outer class we will use a framing class and an outer class notions interchangeably.
Static nested classes
Static nested classes do not have access to non-static fields and methods of the corresponding framing class which makes them to some extent analogous to static methods simply declared inside the class. Non-static fields and methods may be accessed only through a reference to a framing class instance. In that static nested classes are very similar to any other high-level classes.
Besides that static nested classes have access to any static methods of an external class (including the private ones). Such classes are mainly used for entities logical grouping, encapsulation improvement, and class-space economy. Let’s consider the following example:
public class Question {
private Type type;
public Type getType() { return type; }
public void setType(Type type) { this.type = type; }
public static enum Type {
SINGLE_CHOICE, MULIT_CHOICE, TEXT
}
}
For any question (the class Question) a "question type" notion (the class Type) is straightforward. An alternative approach, namely a higher-level class QuestionType creation, would be much less expressive (at least in the context of a class Question).
Client-side code will look something like the following:
Question.Type type = question.getType();
if (type == Question.Type.TEXT) { ... }
As you have probably noticed we are calling the Type class through its framing class - namely, Question.Type. In case of the example at the beginning of this article, StaticNestedClass creation may be done as follows:
OuterClass.StaticNestedClass instance = new OuterClass.StaticNestedClass();
Static nested classes may be used for one more thing - private static methods testing. For example:
public class ClassToTest {
private static void internalMethod() { ... }
public static class Test {
public void testMethod() { ... }
}
}
The thing is that after the given code is compiled we will get 2 files - ClassToTest.class and ClassToTest$Test.class. Interesting that class ClassToTest will contain no information about its nested class (unless we call nested class’s methods which we do not need for tests), so ClassToTest$Test.class can be simply deleted by a build script later.
Member inner classes
All inner classes in Java may be divided into three types:
- member inner classes;
- local classes;
- anonymous classes.
Member inner classes are associated not with an external class, but rather with its instance. And they have access to all of his fields and methods. For example:
public class Users {
...
public class Query {
private Query() { ... }
public void setLogin(String login) { ... }
public void setCreationDate(Date date) { ... }
public List list() { ... }
public User single() { ... }
}
public Query createQuery() { return new Query(); }
}
So we are having an inner class Query, that is designed for users parametrized search. Query class may encapsulate in itself, for example, all the work with the database. But at the same time it has access to Users class state. Here is an example of how a client-side code may look like:
Users.Query query = users.createQuery();
query.setCreationDate(date);
List users = query.list();
Had the Query class constructor been declared as public, a Query class object could have been created from the outside only through the framing class instance:
Users users = new Users();
Users.Query query = users.new Query();
An inner class cannot have any static declarations. Instead of that the static methods may be defined inside the framing class. Besides that such classes cannot contain enums declarations inside.
One important notice - interfaces in Java cannot be instantiated. Therefore it is impossible to declare a non-static inner interface since there will be no object for external class instance association. For example, the following code:
public class OuterClass {
public interface ImNonStaticInterface { ... }
}
will be interpreted as:
public class OuterClass {
public static interface ImNonStaticInterface { ... }
}
meaning that static modifier will be added implicitly.
As it was already mentioned before, member inner class has access to all fields and methods of its framing class. Let’s consider the following code snippet:
public class OuterClass {
public void method() { ... }
public class InnerClass {
public void method() { ... }
public void anotherMethod() {
method();
}
}
}
method() call from anotherMethod will call an InnerClass method. The following construction should be used to access the framing class method - OuterClass.this.method().
Local inner classes
Local classes are defined inside a Java code block. On practice the definition most frequently takes place inside some other class’s method. Even though local class may be defined inside both static and non-static initialization blocks.
Local inner class usage example:
public class Handler {
public void handle(String requestPath) {
class Path {
List parts = new ArrayList();
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);
...
}
...
}
}
This code (with some modifications though) was taken from a real project and is used for get web requests processing. It introduces new abstraction which is convenient to work with from inside the method and which is not needed outside of it.
Just like member classes, local classes are associated with the framing class instance and have access to its fields and methods. Apart from that local classes have access to local variables as well as parameters of the method if they are declared with a final modifier.
But local classes have number of restrictions though:
- They are visible only inside the block containing their declaration;
- They cannot be declared as private, public, protected or static;
- They cannot contain static definitions or fields, methods or classes. Constants are the only exception here (static final);
By the way, interfaces cannot be defined locally because of the same reasons they cannot be declared inner.
Anonymous inner classes
Anonymous classes play important role in every Java-programmer’s life. Those are local classes without their names.
Classic example of an anonymous class:
new Thread(new Runnable() {
public void run() {
...
}
}).start();
A thread is created on the basis of an anonymous class and is run by the start method of the Thread class. Anonymous class creation syntax is based on a new operator being used with some other class (interface) name and the body of the newly created anonymous class. The main limitation on the anonymous classes usage is the constructor description impossibility since the class doesn’t have a name. Arguments provided inside parentheses are automatically used with the base class constructor which matches them. For example:
class Clazz {
Clazz(int param) { }
public static void main(String[] args) {
new Clazz(1) { }; // correct creation of anonymous class
new Clazz() { }; // incorrect creation of anonymous class
}
}
Since an anonymous class is a local class as well, all the local class restrictions apply to it.
Anonymous classes usage is justified in many cases, in particular when:
- Class body is too short;
- Only one instance of the class is needed;
- Class is used exactly where it’s created or right after its creation place;
- Class name is doesn’t matter and doesn’t facilitate the code understanding.
That is probably it. It should be probably mentioned that if all the opportunities that nested classes provide are used wisely, your code will become cleaner, shorter and much more understandable.
Recommended reading:
Войдите чтобы поставить Нравится
Войдите чтобы прокомментировать