Skip to content

The aim of this project is to show case the differences between Imperative and declarative approach of coding. Another focus is on different functions available in the Stream API as well as some pros and cons of some of these approaches.--

Notifications You must be signed in to change notification settings

syedumerahmedcode/java-functional-programming

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

java-functional-programming

Table of content

Introduction

The aim of this project is to show case the differences between Imperative and declarative approach of coding. Another focus is on different functions available in the Stream API as well as some pros and cons of some of these approaches.

Out Of Scope

Since only basic implementation of various functions from stream API is targeted for this project and the main idea is just to see them in action, unit tests are out of scope.

Explanation Declarative Approach

Our normal approach before Java 8 was to program via Imperative Programming but lambdas work via Declarative programming.

Declarative programming: Declarative programming is a programming paradigm that expresses the logic of a computation without describing its control flow. In simple words, Declarative Programming is like asking your friend to draw a landscape. You don’t care how they draw it, that’s up to them.

whereas

Imperative Programming: imperative programming is a programming paradigm that uses statements that changes a program's state. In simple words, Imperative Programming is like your friend listening to Bob Ross telling them how to paint a landscape. While good ole Bob Ross isn’t exactly commanding, he is giving them step by step directions to get the desired result.

Explanation Functional Interface

A functional interface is an interface which has exactly one abstract method.

Please note the following from JavaDoc:

Since default methods have an implementation, they are not abstract. If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface's abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.

Let us see the six basic function interfaces.

Interface Signature Examples
UnaryOperator T apply(T t) String::toLowerCase, Math::tan
BinaryOperator T apply(T t1, T t2) BigInteger::add, Math::pow
Function<T, R> R apply(T t) Arrays::asList, Integer::toBinaryString
Predicate<T, U> boolean test(T t, U u) String::isEmpty, Character::isDigit
Supplier T get() LocalDate::now, Instant::now
Consumer void accept(T t) System.out::println, Error::printStackTrace

Rules for functional programming

Pure functional programming has a set of rules that one must follow:

  • No state, meaning that the function must not depend on or change the state of a variable/object outside the boundary of the function.
  • Pure functions, meaning that the function should have everything encapsulated within it and it should not depend on something outside the boundary of the function, for example, some form of global state.
  • No side effects outside of the boundary of the function.
  • Higher order functions: A function is considered a higher order functions if one of the following two conditions is true.
  1. The function takes one or more functions as parameters. For example, Callback
  2. The function returns another function as result. For example, combinator pattern.

Explanation Callback

A callback is some code that you pass to a given method, so that it can be called at a later time.

Reference: The source code for the Callback is present in Callback.java class.

Explanation Consumer

Consumer: Represents an operation that accepts a single input argument and returns no result. It is similar to a void function when compared to imperative implementation.

BiConsumer<T,U>: Represents an operation that accepts two input arguments and returns no result.

Consumer is implemented in Consumer class in the project. In the consumer class, a consumer object is created which takes Customer as input and prints the information on the command line. In the main method, the consumer is called via accept() function.

Reference: The source code for the Consumer is present in _Consumer.java class.

  • Add a picture linking imperative and declarative approach together.

Explanation Function

Function<T,R>: Represents a function that accepts one argument and produces a result.

BiFunction<T,U,R>: Represents a function that accepts two arguments and produces a result.

Function is implemented in the Function class in the project. In the function class, a function object multiplyByTen is created which takes integer as input and returns a Integer as output. In the main method, the function is utilized by calling apply() function.

Similarly, a BiFunction incrementByOneAndMultiplyBiFunction is also created which takes two integers as input and returns an output.In the main method, andThen() along with apply() function.

Reference: The source code for the Function is present in _Function.java class.

  • Add a picture linking imperative and declarative approach together.

Explanation Optional

A container object which may or may not contain a non-null value. It contains several methods such as isPresent() and get(). If a value is present, isPresent() will return true and get() will return the value.

Additional methods that depend on the presence or absence of a contained value are provided, such as orElse() (return a default value if value not present) and ifPresent() (execute a block of code if the value is present).

Optional is implemented in the Optional class in the project. In the main method, the code

final Optional<Object> empty = Optional.empty();

means that Optional object is empty. Due to this, isPresent() returns false whereas isEmpty() returns true.

Similarly, the code

final Optional<String> hello = Optional.of("hello");

creates an optional object of hello. Due to this, isPresent() returns true whereas isEmpty() returns false.

The code

final Optional<String> hello2 = Optional.ofNullable(null);

delivers a null value. Hence, when calling

hello2.orElse("World");

System.out.println() prints 'World' to command line.

ifPresentOrElse(): Using this method, one can define the logic if the value is present or respond accordingly if it is null. Logically speaking, it works like a ternary operator.

Reference: The source code for the Optional is present in _Optional.java class.

  • Add a picture linking imperative and declarative approach together.

Explanation Predicate

Predicate: Represents a predicate(boolean-valued function) of one argument. Basically, it evaluates a condition to check if it results in true or false. Similarly,

BiPredicate<T,U>: Represents a predicate (boolean-valued function) of two arguments.

Predicate is implemented in Predicate class in the project. In the predicate class, a predicate object

static Predicate<String> isPhoneNumberValidPredicate = phoneNumber -> 
				phoneNumber.startsWith("07") && 
				phoneNumber.length() == 11;

is created which takes phone number as input and checks if the conditions are fulfilled. In the main method, the predicate is called via test() function. Please note that two or more predicates can be combined using .and() or .or() functions.

Reference: The source code for the Predicate is present in _Predicate.java class.

  • Add a picture linking imperative and declarative approach together.

Explanation Supplier

Supplier: Represents a supplier of results. It takes no input and gives an object/list of objects as output.

Supplier is implemented in Supplier class in the project. In the supplier class, a supplier object

static Supplier<String> getDBConnectionUrlsSupplier = () 
			-> DUMMY_DATABASE_CONNECTION_URL;

is created which nothing as input and delivers a String as output. In the main method, the supplier is called via get() function.

Reference: The source code for the Supplier is present in _Supplier.java class.

  • Add a picture linking imperative and declarative approach together.

Explanation Lambdas

  • To be defined

Explanation Map

  • To be defined

Explanation Stream

  • To be defined

Explanation Parallel Stream

  • To be defined

Explanation Combinator Pattern

  • To be defined

Explanation Other Important Points

// Assignment context Predicate p = String::isEmpty; Here we specify the predicate as an empty string . Similar to String p="";

// Method invocation context stream.filter(e -> e.getSize() > 10)... Here e--> is the return type e.size()>10 is the logic of the method

// Cast context stream.map((ToIntFunction) e -> e.getSize())... Here, (ToIntFunction) is used to transform the return type to Integer.

Technologies Used

  • Java 11
  • To be defined

Prerequisities

  • To be defined

Commands

  • To be defined

References

  • 1: Java functional package (JavaDoc)
  • 2: Java Stream (JavaDoc)
  • 3: Functional Programming in Java - Full Course (YouTube)
  • 4: Java Optionals | Crash Course (YouTube)
  • 5: Get a Taste of Lambdas and Get Addicted to Streams by Venkat Subramaniam (Youtube)
  • 6: Java 8 STREAMS Tutorial (YouTube)
  • 7: Declarative vs Imperative Programming
  • 8: Java 8 Tutorials(From Mkyong)
  • 9: What is a callback function?
  • 10: Optional (JavaDoc)

Contact Information

How to reach me? At github specific gmail account. Additionally, you can reach me via Linkedin or at Xing

About

The aim of this project is to show case the differences between Imperative and declarative approach of coding. Another focus is on different functions available in the Stream API as well as some pros and cons of some of these approaches.--

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages