In certain scenarios, we might want to define an implementation of an interface or a subclass without having to create a whole new class and Java file.
Anonymous classes allow us to both define a class and create an instance of it at the same time. These classes, as expected, don’t have a name and you can’t use them more than once.
Let’s use our filter example as a guide.
In our previous example, we defined a Filter<Integer>
as follows:
Filter<Integer> filterToUse = new GreaterThanFiveFilter();
And this is fine, we defined our GreaterThanFiveFilter
as a named class and we’re instantiating it here.
We could achieve the same with an anonymous class as shown below:
Filter<Integer> filterToUse = new Filter<Integer>() {
@Override
public boolean filter(Integer element) {
if (element > 5) {
return true;
}
return false;
}
};
Here, we’re both:
-
Defining an anonymous class via the
new Filter<Integer>() { … }
statement, and inside the braces we implement the methods defined in theFilter
interface, in our case, only thefilter
method. -
And, as we’re using the
new
keyword, creating an instance of that anonymous class and assigning it to thefilterToUse
variable.
Both classes, the GreaterThanFiveFilter
and our anonymous class provide the same logic and we could choose to use one or the other.
Our example with the anonymous class
Using the new approach, our example code would look as follows:
In the example code run the JavaListWithAnonymousFilterApp
|
public static void main(String[] args) {
// our input list of numbers
List<Integer> numbers = Arrays.asList(1, 2, 3, 10, 11, 12, 0);
// define the filter we want to use as an anonymous class
Filter<Integer> filterToUse = new Filter<Integer>() {
@Override
public boolean filter(Integer element) {
if (element > 5) {
return true;
}
return false;
}
};
// call our new `filterNumbers` static method to do the filtering
List<Integer> filtered = filterNumbers(numbers, filterToUse);
// print out the filtered list
for (Integer number : filtered) {
System.out.println(number);
}
}
private static List<Integer> filterNumbers(List<Integer> numbers, Filter<Integer> filterToUse) {
List<Integer> filtered = new ArrayList<>();
// filter the list using the provided `filterToUse`
for (Integer number : numbers) {
if (filterToUse.filter(number)) {
filtered.add(number);
}
}
return filtered;
}
Output:
10
11
12
Access to local variables from the enclosing scope and class
Anonymous classes have direct access to variables in the enclosing scope and class as follows:
-
Local variables (as in, the ones declared in methods) as long as they’re final (using the
final
keyword explicitly) or they’re effectively final (thefinal
keyword isn’t used but their value isn’t assigned more than once). -
Member variables in the enclosing class.
For example, let’s assume that in our example filter we want to specify the minimum value using a local variable instead of setting it to 5 directly (for example, maybe we want the user to define it). In that case, we could do the following:
In the example code run the JavaListWithAnonymousFilterAndLocalVarApp
|
//...
final int minimum = 10;
// define the filter we want to use as an anonymous class
Filter<Integer> filterToUse = new Filter<Integer>() {
@Override
public boolean filter(Integer element) {
if (element > minimum) {
return true;
}
return false;
}
};
//...
Output:
11
12
Here, you can see that the anonymous class is reading the value of the local variable minimum
that is outside the anonymous class' scope.
When should anonymous classes be used?
There is no hard-rule, but in general use them when the anonymous class you’re defining is only meant to be used once and is closely related to the class where it will be defined.
If you need to re-use the anonymous class in several places, you’ll be better off having a named class rather than redefining the anonymous class in multiple places in your code.