These are notes from a course I purchased and followed.
Imperative example (traditional Java) |
Functional example (Java 8 and beyond) |
Comments |
Output |
|
Every instruction is written explicitly, uses loops and conditions.
|
Able to concentrate on what's needed rather than on how to do it.
|
Step by step—what each does: _ // for each/over the stream of numbers // but, only even numbers, ... // convert number to int // and add to sum! _ |
What is output from both: Test: test ----------------------------- Numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Test: testCalculateSum ----------------- 30 |
In the interest of full disclosure, my test code looks like this:
|
|
A pure function in functional programming follows two rules:
That is, if they are defined using Function< ?, ? > function.
In Java functional programming, it's possible to return first-class functions as objects in a variable or in a function return. In this, Java becomes a bit more like C.
First-class functions are created according to this example (which involves a lambda expression). Applying the function is also shown:
Function< Integer, Integer > square = x -> x * x; System.out.println( "The square of 5 is %d.", square.apply( 5 ) );
Just as in C, functions in Java are treated like variables in that they can be assigned to a variable, get passed as arguments to other functions and get returned from other functions.
A higher-order function is one that can take another function as an argument and/or returns a function as its result. Here are two examples:
static void applyFunction( Function< Integer, Integer > function, int value )
{
System.out.println( "Result: " + function.apply( value ) );
}
static Function< Integer, Integer > createMultiplier( int factor )
{
return x -> x * factor;
}
static List< Integer > applyFunctionToList( List< Integer > numbers, Function< Integer, Integer > function )
{
return numbers.stream()
.map( function ) // (apply function to each element)
.collect( Collectors.toList() );
}
static void main( String[] args )
{
List< Integer > numbers = Arrays.asList( 1, 2, 3, 4, 5 );
Function< Integer, Integer > doubleValue = x -> x * 2;
List< Integer > doubledNumbers = applyFunctionToList( numbers, doubleValue );
System.out.println( "Doubled numbers: " + doubledNumbers;
}
Asserted advantages of higher-order functions are:
Pure functional programming follows strict rules to ensure that functions are predictable, resusable and maintainable. There must be...
A lambda expression is simply a function without a name, also called an anonymous function. It can be used as an argument to function where a function is expected. Lambda expressions facilitate functional programming and simplify development.
Principally, the purpose of the lambda expression is to provide an implementation for functional interfaces.
Remember that a functional interface is one that contains exactly one abstract method (though it may also contain default and static methods).
The lambda expression is constructed syntactically (lambda expression syntax) as:
parameters linking to lambda body
( arguments ) -> statement
or
( arguments ) -> { statements }
First, the object-oriented way (later to be compared to the functional programming way):
interface Shape
{
void draw();
}
class Rectangle implements Shape
{
@Override
public void draw() { System.out.println( "Rectangle drawn" ); }
}
class Square implements Shape
{
@Override
public void draw() { System.out.println( "Square drawn" ); }
}
class Circle implements Shape
{
@Override
public void draw() { System.out.println( "Circle drawn" ); }
}
Shape rectangle = new Rectangle();
Shape square = new Square();
Shape circle = new Circle();
public static void main( String[] args )
{
rectangle.draw();
square.draw();
circle.draw();
}
The functional-programming way:
interface Shape
{
void draw();
}
// (remember: lambda is arguments (none) -> (one) statement
Shape rectangle = () -> System.out.println( "Rectangle drawn" );
Shape square = () -> System.out.println( "Square drawn" );
Shape circle = () -> System.out.println( "Circle drawn" );
public static void main( String[] args )
{
rectangle.draw();
square.draw();
circle.draw();
}
As we transition (the OOP code) to functional programming, we change some things. It's as if we copy down the Java code:
class Rectangle implements Shape1 { @Override2 public3 void4 draw5() { System.out.println( "Rectangle drawn" ); } }
...and convert it over to functional:
@FunctionalInterface // (unnecessary)
interface Calculator
{
int calculator( int a, int b );
}
public class CalculatorExample
{
public static void main( String[] args )
{
// imagine all the class boilerplate to be created above (missing here)
// in support of what's below:
// Calculator addition = new Addition;
// System.out.println( addition.calculate( 10, 20 ) );
//
// Calculator subtraction = new Subtraction;
// System.out.println( subtraction.calculate( 20, 10 ) );
//
// Calculator multiplication = new Multiplication;
// System.out.println( multiplication.calculate( 10, 20 ) );
//
// Calculator division = new Division;
// System.out.println( division.calculate( 20, 10 ) );
// instead, let's use lambda expressions:
Calculator addition = ( a, b ) -> a + b;
System.out.println( addition.calculate( 10, 20 ) );
Calculator subtraction = ( a, b ) -> a - b;
System.out.println( subtraction.calculate( 20, 10 ) );
Calculator multiplication = ( a, b ) -> a * b;
System.out.println( multiplication.calculate( 10, 20 ) );
Calculator division = ( a, b ) -> a / b;
System.out.println( division.calculate( 20, 10 ) );
}
}
We're referring to the calculator code above.
interface Calculator
{
int calculator( int a, int b );
}
public class CalculatorExample
{
public static void main( String[] args )
{
// replace what's above with these passing the lambda as third argument:
System.out.println( calculate( 10, 20, ( a, b ) -> a + b ) );
System.out.println( calculate( 20, 10, ( a, b ) -> a - b ) );
System.out.println( calculate( 10, 20, ( a, b ) -> a * b ) );
System.out.println( calculate( 20, 10, ( a, b ) -> a / b ) );
}
// but, we have to create this method:
private static int calculate( int a, int b, Calculator calculator )
{
return calculator.calculate( a, b );
}
}
class ThreadDemo implements Runnable
{
@Override
public void run()
{
System.out.println( "run() method called..." );
}
}
public class LambdaThread
{
public static void main( String[] args )
{
ThreadDemo thread = new Thread( new ThreadDemo() );
thread.start();
}
}