DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

SBOMs are essential to circumventing software supply chain attacks, and they provide visibility into various software components.

Related

  • Memory Leak Due To Mutable Keys in Java Collections
  • Modernizing Apache Spark Applications With GenAI: Migrating From Java to Scala
  • Squid Game: The Clean Code Trials — A Java Developer's Survival Story
  • Jakarta EE 11 and the Road Ahead With Jakarta EE 12

Trending

  • Tracing Stratoshark’s Roots: From Packet Capture to System Call Analysis
  • Jakarta EE 11 and the Road Ahead With Jakarta EE 12
  • From Drift to Discipline: Operating Model for Regaining Enterprise Cloud Control
  • Modernizing Apache Spark Applications With GenAI: Migrating From Java to Scala
  1. DZone
  2. Coding
  3. Java
  4. Java Lambda Expressions: Functions as First-Class Citizens

Java Lambda Expressions: Functions as First-Class Citizens

Whether you're migrating to Java 8 or just want a refresher, here's an excellent primer of lambda expressions and how they help bridge OOP and FP in Java.

By 
Yogen Rai user avatar
Yogen Rai
·
Jun. 20, 18 · Tutorial
Likes (18)
Comment
Save
Tweet
Share
22.3K Views

Join the DZone community and get the full member experience.

Join For Free

Since Java 8, a lot of boilerplate code can be replaced with lambda expressions in our codebase. I really want to put out an article about Streams in Java, but since that carries real value only if we combine them with lambda expressions, I want to write about some ideas about playing around with lambda expressions first.

Functional Interfaces in Java

A functional interface is an interface with a single abstract method, also known as SAM (Single Abstract Method) types. The concept was introduced in JDK 8, but there were interfaces prior to JDK 8 that complied with that definition. For example, Comparator has only one method: compare().

We can create our own as:

@FunctionalInterface
interface Calculator<T,R> {
    R calculate(T a, T b);
}


Here, the @FunctionalInterface annotation is checked by the compiler. So, if the interface does not contain exactly one abstract method, there is a compiler error.

It is not necessary to use this annotation when providing a type for a lambda expression, but, like other annotations (@Override, for example), it is a best practice to use it because it tells the compiler to check that it would work — otherwise, it will be overlooked until runtime.

Now, What Is a Lambda Expression?

A lambda expression is an inline implementation of a functional interface, eliminating the need of an anonymous class.

A lambda expression has three parts:

parameters [zero or more]
    ->
code block [if more than one statement, enclosed in curly braces { . . . } ] 
           [may contain free variables; values for these supplied by local or instance vbles]


For example:

(a,b) -> a + b; // given two numbers a and b, return another number with their summation.

(String str) -> System.out.println(str); // prints the given string into console


The equivalent of the first lambda expression is:

new Calculator<Integer, Integer>() {    
    @Override    
    public Integer calculate(Integer a, Integer b) {        
        return a + b;    
    }
};


You can see how lambda expressions help to get rid of the boilerplate code.

Naming Lambda Expressions

Every object in Java has a type; the same is true of lambda expressions. The type of a lambda expression is any functional interface for which the lambda expression is an implementation.

Naming a lambda expression is done by using an appropriate functional interface as its type, like naming any other object. For example:

Calculator<Integer, Integer> adder = (a,b) -> a + b;


The name of this lambda expression is adder of type Calculator.

Note: I am omitting the parameter type because if parameter types can be inferred, they can be omitted. So, the above expression with the parameter type would be:

Calculator<Integer, Integer> adder = (Integer a,Integer b) -> a + b;


Using Lambda Expressions

You've seen how inner class approximations can be replaced by lambda expressions, which capture their essential functional nature: Arguments mapped to outputs. Hence, the functions are now first-class citizens. So:

1. A lambda expression can be used as a regular class object to call the method. For example, adder can be used to call the calculate method as:

Integer sum = adder.calculate(3,4); // sum = 7


2. Lambda expressions can be passed as an argument and used to evaluate expressions as:

// ...
printSum(adder);
// ...

static void printSum(Calculator cal) {
    System.out.println(cal.calculate(4,5));
}


3. Lambda expressions can be returned as a return object:

// ...
Calculator<Integer, Integer> multiplier = getCalculatorFunc();
Integer result = multiplier.calculate(6,9));  // 54
// ...
static Calculator getCalculatorFunc() {
    Calculator<Integer, Integer> multiplier = (a, b) -> a * b;
    return multiplier;
}


Conclusion

Now, you can see how it is possible to reap many of the benefits of Functional Programming while maintaining the OO essence of the Java language as a whole. Lambda expressions are the fundamentals for functional programming and writing code concisely with Streams, which I want to continue in the next post. Until then, happy coding!

The source code for the examples presented above is available on GitHub.

Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Memory Leak Due To Mutable Keys in Java Collections
  • Modernizing Apache Spark Applications With GenAI: Migrating From Java to Scala
  • Squid Game: The Clean Code Trials — A Java Developer's Survival Story
  • Jakarta EE 11 and the Road Ahead With Jakarta EE 12

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: