Anonymous inner classes in Java

on waitingforcode.com

Anonymous inner classes in Java

You're still doing Java/C#/JavaScript/Python/PHP... and need a wind of change? I was like that 4 years ago. I changed then to the data engineering field and it solved my existential problems :) If you want to follow my path, I prepared a course that will help you with that! Join the class!
Sometimes when you want to write a code "proof of concept", you can define classes without names, inside methods. It's for example the case of very common new Thread(new Runnable() {}).start() which launches new Java's thread. In this article we'll focus on these classes, called anonymous inner classes.

The first part of this article will explain the being reason of anonymous inner classes. The next, and at the same the last part, will concern practical samples of use.

What is anonymous inner class in Java ?

As its name indicates, an anonymous inner class is a class without name. It's destined to one-shot use. Imagine following situation: you have an unit test that checks if Java's file from some directories are compiled correctly. Firstly, it has to find files ending with .java extension. To do this, we can implement java.io.FilenameFilter interface. And now we can do it through some different ways:
- declare public/private class and initialize it in test method
- use lambdas
- use anonymous inner class

Because this feature (Java's file filter) will be used only at one place and the projects runs still under Java 7, the last choice comes naturally as the right one. Let's see it:

public void example() {
  File dir = new File("/home/bartosz/tmp/test_project");
  String[] files = dir.list(new FilenameFilter() {
    @Override
    public boolean accept(File file, String name) {
      return name.endsWith(".java");
    }
  });
  for (String f : files) {
    System.out.println("Got file: "+f);
  }
}

In the case of implementation, anonymous inner class is always invoked through interface name, for example: new FilenameFilter, new Runnable etc. When we want to extend some classes, we must use the name of the class to extend. All anonymous inner classes have an access to variables from enclosing class that are finals. Anonymous inner class will always use the variables defined inside it. The anonymous inner classes can't declare the constructors or static fields.

Examples of anonymous inner classes in Java

After this short introduction, we can see an implementation of anonymous inner class. In our example, we'll create an interface WordFilter. Its method filter(String content) will look for blacklisted words in provided content. All blacklisted words will be replaced by "!!!" character. In our application, we'll need this filters two times: for the forum and article's comments. Both places will have different blacklisted words. This study case will be presented through following unit test case:

public class AnonymousClassTest {
  @Test
  public void testBlacklisted() {
      WordFilter forumFilter = new WordFilter() {
        private String pattern = "(?i)(duck|cow|pig)";
        
        @Override
        public String filter(String text) {
          return text.replaceAll(pattern, "!!!");
        }
      };
      String animalText = "Duck is the common name for a large number of species in the Anatidae family of birds, which also includes swans and geese. Cow: Cattle are the most common type of large domesticated ungulates. They are a prominent modern member of the subfamily Bovinae, are the most widespread species of the genus Bos, and are most commonly classified collectively as Bos primigenius. A pig is any of the animals in the genus Sus, within the Suidae family of even-toed ungulates. Pigs include the domestic pig and its ancestor, the common Eurasian wild boar, along with other species";
      String expectedText = "!!! is the common name for a large number of species in the Anatidae family of birds, which also includes swans and geese. !!!: Cattle are the most common type of large domesticated ungulates. They are a prominent modern member of the subfamily Bovinae, are the most widespread species of the genus Bos, and are most commonly classified collectively as Bos primigenius. A !!! is any of the animals in the genus Sus, within the Suidae family of even-toed ungulates. !!!s include the domestic !!! and its ancestor, the common Eurasian wild boar, along with other species";
      assertTrue("Expected text must be the same as fitlered", forumFilter.filter(animalText).equals(expectedText));
      
      WordFilter commentFilter = new WordFilter() {
        @Override
        public String filter(String text) {
          return text;
        }
      };
      String commentText = "This text won't be filtered. We print only original comments";
      assertTrue("Comment's text shouldn't be filtered, but it was", 
        commentFilter.filter(commentText).equals(commentText));
  }
}

interface WordFilter {
    public String filter(String text);
}

As you can see in this code, we implement WordFilter interface to one-shot test. You should never use anonymous inner classes when they will be reused in more than one place. In additionally, prefer to declare anonymous inner classes only when you think that theirs body will be short. Otherwise, you will lose code readability rather than gain it.

Anonymous inner class construction looks like a simple class definition. So, they can extend multiple interfaces, extend public class or implement abstracted ones with the same rules as in the case of a new class declaration.

Thanks to anonymous inner classes, we can improve the code readability by not overloading the application with some of objects used only in one place. They are also very useful when we want to test some implementation quickly, for example, during testing. However, lambdas arrival in Java 8 reduces a lot the utility of anonymous inner classes which can be replaced with even more readable instructions.

Share on: