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.
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?
Reveal 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.
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.
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.
- 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.
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.
Draws the same oval into the graphics context g1 and then into g2The 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.- 1new asks for an object
- 2
Drawable()names the interface the object satisfies - 3The braces supply the class body because no class name is written
- 4
draw(Graphics g)is the required method, with g as its own parameter - 5The 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.
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?
- 1Create the object
The new
Drawable()expression creates an anonymous object whose draw method is the body written in the braces. - 2Bind 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.
- 3First 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. - 4Second 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.
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?
Reveal 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 likeWriting a lambda for an interface that declares two abstract methods, such as Figure above.
Why it failsA lambda expression can only be used with a functional interface, meaning an interface with exactly one abstract method.
Do this insteadCount 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 likeWriting code inside drawTwice that refers to the anonymous class by name.
Why it failsThe anonymous class has no name. The method only sees the parameter figure and calls draw through the Drawable type.
Do this insteadRefer 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 likeBelieving that g inside draw is the same variable as g1 when draw is called with g1.
Why it failsg 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 insteadTrace 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 likeTrying to call the same anonymous Drawable from a different method later in the file.
Why it failsAn anonymous class exists only as the expression in which it is written.
Do this insteadIf 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 likeAssuming an anonymous class adds nothing to the compiled output, the same as a lambda.
Why it failsEach anonymous class is compiled into its own .class file, while a lambda expression does not create one.
Do this insteadAccount for one extra compiled file per anonymous class when reasoning about the program's output.
Practice Choosing and Tracing
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.
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
- 1A lambda expression is a shorthand for a function and can only implement a functional interface, one with exactly one abstract method.
- 2An anonymous class is a nameless class declared and instantiated in one expression, and it can implement any interface or class.
- 3Choose a lambda only after counting the abstract methods; with two or more, an anonymous class is the tool.
- 4An anonymous class adds one .class file per class, while a lambda expression does not.
- 5A 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.