Theory:Anonymous classes properties
As you remember, there are anonymous classes in the world of Java. They don't have name identifiers and they enable you to declare and instantiate a class at the same time. But you didn't think that the whole theory was covered in the first topic, did you? So, today is a special day — you will finally learn all the nuances about anonymous classes!
# How to create?
The most popular way to create an anonymous class is to implement an interface and you have seen it already. So, now let's try something new — let's create an anonymous class that inherits from another class.
Imagine a human: they have a brain and many-many thoughts in it. Every couple of seconds they produce a new thought. And we want to describe it in Java language. So, how will we do it?
abstract class HumanThought {
public void print() {
System.out.println("This is a very important thought.");
}
}
2
3
4
5
This is our superclass HumanThought
that has one method for printing the standard thought. And now anonymous classes will help us. By inheriting from HumanThought
we may have as many different thoughts as we want — and that's all without new .java
files.
All new thoughts would be children of our superclass and will have their own method print:
class Human {
public void takeMilkshake() {
// creation of anonymous class by inheriting from class HumanThought
HumanThought thought = new HumanThought() {
public void print() {
System.out.println("What to take? Chocolate or strawberry milkshake..");
}
};
thought.print();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
And if we wanted to create an actual human and invoke method takeMilkshake
— what would be the output?
public class Main {
public static void main(String[] args) {
Human human = new Human();
human.takeMilkshake();
}
}
2
3
4
5
6
7
8
Of course, you are thinking the right thought:
What to take? Chocolate or strawberry milkshake..
And have you noticed that class HumanThought
is abstract? Yes, it is — don't forget that we can create an anonymous class by inheriting from both: concrete and abstract classes.
But to become a master in programming you should get acquainted with another case of creating anonymous classes: when we create an anonymous class and pass it in a constructor as an argument.
So, what is going on in our example below? Firstly, Thread
is a standard Java class. And if you don't know each other yet, don't be afraid — fruitful cooperation awaits you. And Runnable
is an interface that describes some action — any you want. In our example, Runnable
has an action that only prints a phrase.
Thread
has several constructors, that is a normal situation, and one of its constructors take as an argument an anonymous class that implements the interface Runnable
.
As a result, the anonymous class is passed as an argument of the constructor:
class MyExample {
public static void main(String[] args) {
//Anonymous class is created as a constructor argument
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("Run, Forrest, run!");
}
});
}
}
2
3
4
5
6
7
8
9
10
11
# Learn callbacks by example
Also, often, after creating an instance of an anonymous class we pass it to some method as an argument. In this case, the anonymous class is called a callback. A callback is a piece of executable code that is passed to another code that executes it (performs a call back) at a convenient time. And can we call our previous example a callback? Yes, we can — because the constructor is a special method.
Let's consider an example. There is a special kind of calculator that can only divide numbers. The calculator takes a callback as its argument and executes the callback passing the result of the calculation or an error message.
The Callback
interface has two abstract methods:
interface Callback {
/**
* Takes a result and processes it
*/
void calculated(int result);
/**
* Takes an error message
*/
void failed(String errorMsg);
}
2
3
4
5
6
7
8
9
10
11
12
The class Divider
has only one static method (just an example, the demonstrated technique works with any methods):
class Divider {
/**
* Divide a by b. It executes the specified callback to process results
*/
public static void divide(int a, int b, Callback callback) {
if (b == 0) {
callback.failed("Division by zero!");
return;
}
callback.calculated(a / b);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Of course, in this case, you can perform the division and return the result without any callbacks. In general, though, callbacks can help you in large applications with multiple parts and layers (especially in multithreaded programs).
Calling a method with a callback:
public class CallbacksExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int a = scanner.nextInt();
int b = scanner.nextInt();
Divider.divide(a, b, new Callback() { // passing callback as an argument
@Override
public void calculated(int result) {
String textToPrint = String.format("%d / %d is %d", a, b, result);
print(textToPrint);
}
@Override
public void failed(String errorMsg) {
print(errorMsg);
}
});
}
public static void print(String text) {
System.out.println(text);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
As you can see, we instantiate and pass the callback without any additional variables of the Callback
type. It's a very common practice for working with callbacks, especially if they are small.
The callback captures the static method print
and the local variables a
and b
from its context. The variables a
and b
are effectively final here. (i.e the variables aren't changed you don't need to write the keyword final
).
Let's run the program.
Input 1:
8 2
Output 1:
8 / 2 is 4
Input 2:
10 0
Output 2:
Division by zero!
So, anonymous classes along with the context capture mechanism allow you to transfer logic between parts of your program. They are used as callbacks in large applications and when working with external libraries.
# Restrictions on anonymous classes
And, of course, anonymous classes have some restrictions:
- they cannot have static initializers or interface declarations
- they cannot have static members, except the constant variables (
final static
fields) - they cannot have constructors
Let's consider the following example with an anonymous class SpeakingEntity
. To not have static initializers, static members, or interface declarations — it's not a big deal. But what about not having a constructor? How to initialize our fields?
final String robotName = "Bug";
final int robotAssemblyYear = 2112;
SpeakingEntity robot = new SpeakingEntity() {
static final int MAGIC_CONSTANT = 10;
private String name;
private int assemblyYear;
{ /* instance initialization block for setting fields */
name = robotName;
assemblyYear = robotAssemblyYear;
}
@Override
public void sayHello() {
System.out.println("1010001" + MAGIC_CONSTANT);
}
@Override
public void sayBye() {
System.out.println("0101110" + MAGIC_CONSTANT);
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
And for examples like this one you should be friends with the good old theory! Remember about instance initializers and what they are for? Yeah, instance initializer allows us to substitute a constructor. It runs each time when an object of the class is created. So, fields name
and assemblyYear
will be initialized and our robot will have the name "Bug" and a year of birth!
# Summary
Perhaps you are wondering where to go to see how anonymous classes are used in "real life"? In that case, we may have a suggestion.
Anonymous classes are actively used when writing user interfaces with the standard Java library called Swing. The same with developing a web user interface using Google Web Toolkit (GWT). It is very common to have a lot of listeners that are used just once for one button, so using anonymous classes allows us to avoid writing a lot of classes and having useless files in the development of the code. Some widespread libraries for working through the HTTP protocol also use anonymous classes. For example, this HttpAsyncClient (opens new window).
And the last one — what are the advantages of anonymous classes? Anonymous classes enable you to make code more concise and reduce the number of .java
files. And what about encapsulation? Yes, you are thinking right — each anonymous class has very local scope, it is defined exactly where it is needed and it can never be used anywhere else. So, it definitely increases encapsulation and provides you with a great reason to fall in love with Java!