How to implement callbacks in Java
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
In Java, callbacks are usually implemented by passing an object that exposes a method to be invoked later. Historically that meant interfaces and anonymous classes. In modern Java, it often means a functional interface and a lambda.
The Classic Interface-Based Callback
A callback is simply an agreement: "when this work finishes, call this method." In Java, the most common way to express that agreement is an interface.
Here ResultCallback defines the callback contract, and Worker invokes it when the work is complete.
Why Lambdas Make This Cleaner
Because ResultCallback has one abstract method, it is a functional interface. That means callers can use a lambda instead of writing an anonymous class.
Without lambdas, older Java code looked more like this:
Both forms are valid. The lambda version is simply more concise.
Callbacks for Success and Failure
Real code often needs more than one callback path. A common pattern is separate success and error methods.
This makes the control flow explicit and keeps the caller informed about both outcomes.
Asynchronous Callbacks
Callbacks become especially useful when the work does not finish immediately. For example, a background thread can invoke the callback later.
This pattern is simple, but once concurrency gets more serious, Java developers often move toward CompletableFuture, executors, or reactive APIs instead of building many custom callback interfaces by hand.
When to Prefer CompletableFuture
For modern asynchronous Java code, CompletableFuture can be cleaner than manual callbacks because it supports composition.
This is still callback-style programming, but the callback hooks are part of a richer standard abstraction.
That becomes especially valuable once you need chaining, transformation, timeout handling, or combining several asynchronous results. Manual callback interfaces are fine for simple local APIs, but composition gets messy quickly.
Common Pitfalls
The most common mistake is using a callback interface with too many unrelated methods. Keep the contract focused.
Another mistake is invoking callbacks from background threads without documenting that threading behavior. The caller needs to know what thread the callback runs on.
A third pitfall is using manual callback chains for complex async flows when CompletableFuture or another higher-level abstraction would be clearer.
Summary
- In Java, callbacks are usually implemented with interfaces or functional interfaces.
- Lambdas make single-method callback interfaces concise and readable.
- Separate success and error callbacks when the operation can fail.
- For asynchronous work, document which thread invokes the callback.
- For more complex async composition,
CompletableFutureis often better than hand-built callback chains.
Related reading
- How to implement infinity in Java?
- How to import a .cer certificate into a java keystore?
- How to import a GIT non-Eclipse Java project into Eclipse?
- How to import a jar in Eclipse?
- How to import an existing X.509 certificate and private key in Java keystore to use in SSL?
- How to increase the number of messages consumed by Spring Kafka Consumer in each batch?
- How to initialize a Guava ImmutableMap?
- How to initialize an array in Java?

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.