Skip to content

Instantly share code, notes, and snippets.

@bdkosher
Last active August 29, 2015 14:16
Show Gist options
  • Save bdkosher/72a4dcfc3f921107cfbe to your computer and use it in GitHub Desktop.
Save bdkosher/72a4dcfc3f921107cfbe to your computer and use it in GitHub Desktop.
Blog post about adding methods to anonymous types in Java

Methods in Anonymous Java Object Types

Java syntax allows you to create instances of anonymous types without much boilerplate. This approach is commonly use to implement single-use instance of Single Abstract Method-style interfaces such as Comparable and Runnable:

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, world!");
    }
}).start();

In the code above, we simultaneously create an anonymous subtype of Runnable and an instance of that subtype, which we immediately pass to the Thread constructor.

This pattern also works with non-abstract types, most notably with the List instance initializer pattern.

List<String> list = new ArrayList<>() {{
     add("Hello");
     add("world");
}};

The anonymous subtype of ArrayList has an initializer block that invokes its add method twice, thus populating the list on initialization in a syntactically brief manner.

You can even add NEW methods to these anonymously-typed instances, for example:

Object o = new Object() {
     public int getSecret() {
        return 9;
     }
};

But what's the point of doing this? The Object reference cannot see the new method because it's not defined in the base Object type. It's effectively invisible, which is probably why I've never seen this technique used in a legitimate application...until today.

Reflection FTW

In a Spring Boot application, I was using Freemarker to implement the view. I wanted to provide an object in the model which had an "id" property that returned a random number. Here's the controller method implementation:

@RequestMapping(value="entity/{id}", produces="text/html")
ModelAndView retrieveEntity(@PathVariable String id) {
    ModelAndView mav = new ModelAndView("entity_as_html.ftl");
    mav.getModelMap().put("random", new Object() {
        final Random random = new Random();
        public int getId() {
            return random.nextInt();
        }
    });
    return mav;
}

And here's a relevant snippet from the Freemarker template:

<element id="${random.id?string("#")}"/>

Freemarker is essentially doing duck typing here--it doesn't care what actual type that "random" is, only that it has a property named "id." Thus, I can provide to the model an object of whatever type I wish that has an "id" property, and I felt it most convenient for the type of that object to be an anonymous sub-type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment