Concepts / Anonymous Classes

Anonymous Classes

A lambda expression is a shorthand for a function, and it can only be used with a functional interface, meaning an interface with exactly one abstract method.

  • Programming

A Tempting Belief About Lambdas

Once you learn that a lambda expression is a short way to write a function, a natural assumption follows: if a lambda can stand in for a piece of behavior, it should be usable wherever behavior is passed around. Many learners therefore believe that a lambda expression can be used with any interface or class. This article tests that belief, shows where it breaks, and then explains what lambda expressions and anonymous classes have in common and where they differ.

What do you think happens?

Can a lambda expression implement an interface that declares two abstract methods?

  • Yes, the lambda defines the body of both methods

  • No, a lambda can only implement an interface with exactly one abstract method

Reveal answer
Answer:

No, a lambda can only implement an interface with exactly one abstract method.

The source defines a functional interface as an interface with exactly one abstract method. A lambda expression is a shorthand for a function, and it can only be used with a functional interface. A lambda supplies one function body, so it has no way to say which of two abstract methods it is defining.

The belief usually grows out of a true observation. Anonymous classes really can be used with any interface or class, so a learner who has only seen anonymous classes may assume that every shorter form has the same reach. The mistake is to treat the two constructs as interchangeable in scope. The source explicitly separates them: the lambda is restricted, and the anonymous class is general. The source describes a lambda expression as a shorthand for a function or method, and a shorthand is only useful in places where the longer form was already allowed. So the question to ask is which places those are.

Interfaces as Promises

Sometimes a method needs a piece of behavior from its caller, not just a number or a string. Java lets you express that need with an interface, which names a set of methods a caller must supply. The method receiving the behavior never needs to know which concrete class supplies it. It only needs to know that the object promises the required methods.

The source uses an interface called Drawable to make this concrete. Read the declaration as a promise: anything claiming to be a Drawable has a draw method that accepts a Graphics object. The declaration contains no body for draw. The body belongs to whichever class implements the interface. Classes are allowed to implement interfaces, and that mechanism is what the rest of this article depends on.

java
Definition

An interface declares a set of methods that implementing classes must provide, and a variable of the interface type can refer to any implementing object.

The Method That Receives a Drawable

The method that consumes a Drawable takes two graphics contexts and one drawable figure, then asks the figure to draw itself into each context in turn. The method is written against the interface, so the caller decides what the figure does, while the method decides how many times and in which contexts it happens.

java

Notice what a prediction about this method depends on. You do not need to know the name of the class behind figure. You only need the fact that figure is a Drawable, so calling draw on it is legal. That is the point of the design: the method is general, and the specific drawing behavior arrives from outside.

Counting Abstract Methods Before Choosing

The easiest way to see the lambda restriction is to compare two interfaces side by side. The first has a single abstract method, which makes it functional. The second has two abstract methods, which makes it non-functional. The source states that checking the number of abstract methods in the target interface is the first step in deciding between a lambda and an anonymous class.

java
Notes on the code
  • Shape has one abstract method, so a lambda can implement it
  • Figure has two abstract methods, so a lambda cannot implement it and an anonymous class is needed

The Drawable interface in the source follows the first pattern. Drawable has one abstract method, so the anonymous class that the source first presents can be replaced by a lambda expression. Both forms rely on the same fact: Drawable has one abstract method, so the lambda has a single method to define.

Lambdas Versus Anonymous Classes

The two practical differences between the forms are the interface requirement and the compiled output. The first is about what you may implement. A lambda expression needs a functional interface, one with exactly one abstract method. An anonymous class can be used with any interface or class, so it is the more general tool. The second difference is about files. Each anonymous class is compiled into its own .class file, which means one extra file for each anonymous class in the program. A lambda expression does not create an extra .class file, and the source presents this as an advantage of the lambda approach.

Functional interfaceonlyexactly one abstract methodAny interface orclassone or many abstractmethodsNo extra .class filecompiled inlineOne extra .class fileper anonymous class
Which form can implement a given interface, and what does the compiler produce for each form?

Reading new Drawable() Piece by Piece

An anonymous class is a class without a name that is declared and instantiated in one expression, and it can implement an interface by supplying the required method body. Read the expression from left to right. The keyword new asks for an object. Drawable() names the interface the object will satisfy, and the empty parentheses work the same way as any instance creation. Because no class name appears, the braces that follow supply the entire class body. Inside those braces, the method draw is written with the parameter g, and its body is the work the figure performs. The anonymous class exists only as this expression, so you use it exactly where the object is needed.

java
Output
Draws the same oval into the graphics context g1 and then into g2
newasks for a new objectDrawable()the interface the objectsatisfies{ class body }supplies the whole class,since no name is givendraw(Graphics g)the method implementing theinterfaceg.fillOval(10, 10,100, 100)the drawing work itself
Which piece of new Drawable() { ... } names the interface, which holds the class body, and which part does the actual drawing work?
Code walkthrough
The expression starts with new, which asks for an object. Drawable() names the interface that the new object will satisfy. The braces that follow are the anonymous class body, because there is no class name to refer to. Inside the body, draw(Graphics g) is the implementation of the interface's single method. Its body, g.fillOval(10, 10, 100, 100), is the behavior the caller is supplying.
  1. 1
    new asks for an object
  2. 2
    Drawable() names the interface the object satisfies
  3. 3
    The braces supply the class body because no class name is written
  4. 4
    draw(Graphics g) is the required method, with g as its own parameter
  5. 5
    The body of draw does the work each time draw is called

Tracing the Anonymous Object Through drawTwice

Anonymous classes are often used for actual parameters, meaning the values supplied at a call site rather than declared variables. The third argument to drawTwice is not a variable that was declared earlier. It is the anonymous Drawable, created directly inside the argument list. Each actual argument is bound to the parameter in the same position. The first two arguments are Graphics objects, so they land in g1 and g2. The third argument is the anonymous object, so it lands in figure. Inside drawTwice, the name figure refers to that object, and the method never mentions the anonymous class by name.

Call sitedrawTwice(g1, g2, newDrawable() {...})g1first Graphics contextdrawTwice parametersg1, g2, figurefigure.draw(g1)runs the anonymous drawbody firstg2second Graphics contextfigure.draw(g2)runs the same body secondAnonymous Drawablecreated inside the argumentlistargument 1argument 2argument 3bound to g1bound to g2bound to figurefirst callsecond call
Where is the anonymous object created, how does it move into the parameter figure, and which implementation runs when drawTwice calls draw?
Worked example

Tracing one call to drawTwice

Given the call drawTwice(g1, g2, new Drawable() { ... }), which graphics context does each call to draw use, and in what order do the calls happen?

  1. 1
    Create the object

    The new Drawable() expression creates an anonymous object whose draw method is the body written in the braces.

  2. 2
    Bind the arguments

    g1 is bound to the first parameter, g2 to the second, and the anonymous object to figure, since each argument goes to the parameter in the same position.

  3. 3
    First call

    figure.draw(g1) runs the anonymous draw body with its own parameter g set to the value of g1, so the oval is drawn into the first context.

  4. 4
    Second call

    figure.draw(g2) runs the same body with g set to the value of g2, so the oval is drawn into the second context.

Result

The anonymous draw body runs twice, first with g1 and then with g2. The name g inside draw is a separate parameter from g1 and g2, and the value passed on each call is what connects them.

Predicting the Order of Calls

What do you think happens?

In drawTwice(g1, g2, new Drawable() { ... }), which context does the first draw call use?

  • g2, because the anonymous object is created last

  • g1, because figure.draw(g1) is the first call inside drawTwice

Reveal answer
Answer:

g1, because figure.draw(g1) is the first call inside drawTwice.

drawTwice asks figure to draw itself into each context in turn, starting with the first argument. The order is fixed by the body of drawTwice, not by where the anonymous object was written.

Mistakes That Break Anonymous Implementations

  • Common mistake

    Assuming a lambda works with any interface

    What it looks like

    Writing a lambda for an interface that declares two abstract methods, such as Figure above.

    Why it fails

    A lambda expression can only be used with a functional interface, meaning an interface with exactly one abstract method.

    Do this instead

    Count the abstract methods first. With one, a lambda is allowed. With two or more, use an anonymous class.

  • Common mistake

    Expecting the method to name the anonymous class

    What it looks like

    Writing code inside drawTwice that refers to the anonymous class by name.

    Why it fails

    The anonymous class has no name. The method only sees the parameter figure and calls draw through the Drawable type.

    Do this instead

    Refer to the object through its parameter name and call the interface method on it.

  • Common mistake

    Confusing the parameter g with the arguments g1 and g2

    What it looks like

    Believing that g inside draw is the same variable as g1 when draw is called with g1.

    Why it fails

    g is a parameter of the anonymous draw method, and g1 is a parameter of drawTwice. The value passed on each call is what links them.

    Do this instead

    Trace the argument in the call that is currently executing to find which context is in use.

  • Common mistake

    Expecting a reusable named object

    What it looks like

    Trying to call the same anonymous Drawable from a different method later in the file.

    Why it fails

    An anonymous class exists only as the expression in which it is written.

    Do this instead

    If the same behavior is needed in several places, declare a named class that implements the interface.

  • Common mistake

    Forgetting the extra .class file

    What it looks like

    Assuming an anonymous class adds nothing to the compiled output, the same as a lambda.

    Why it fails

    Each anonymous class is compiled into its own .class file, while a lambda expression does not create one.

    Do this instead

    Account for one extra compiled file per anonymous class when reasoning about the program's output.

Practice Choosing and Tracing

EASY

An interface named Sizable declares one method, int size(). A second interface named Report declares two methods, String title() and int pages(). For each interface, decide whether a lambda expression or an anonymous class fits, then explain how you counted the abstract methods to decide.

Hints
  • Count only abstract methods, one for each method the interface requires.
  • A lambda expression needs exactly one abstract method.
MEDIUM

A method process(Drawable first, Drawable second) calls first.draw(...) and then second.draw(...). A caller passes two different anonymous Drawable objects. Explain which object's draw body runs first and how you know, using the rule that each call on a parameter runs the implementation of the object bound to that parameter.

Hints
  • Trace which argument is bound to first and which to second.
  • The order of calls inside process decides which body runs first.

Key Takeaways for Anonymous Classes

  1. 1
    A lambda expression is a shorthand for a function and can only implement a functional interface, one with exactly one abstract method.
  2. 2
    An anonymous class is a nameless class declared and instantiated in one expression, and it can implement any interface or class.
  3. 3
    Choose a lambda only after counting the abstract methods; with two or more, an anonymous class is the tool.
  4. 4
    An anonymous class adds one .class file per class, while a lambda expression does not.
  5. 5
    A method that receives an interface-typed parameter runs whichever implementation was passed in, and each call follows the argument bound to that parameter.

At a glance

  • A lambda expression is a shorthand for a function and can only implement a functional interface, one with exactly one abstract method.
  • An anonymous class can implement any interface or class, so it is the more general tool.
  • Counting abstract methods is the first step in choosing between a lambda and an anonymous class.
  • Each anonymous class produces its own .class file, while a lambda expression does not.