Programmers are lazy by nature just like any other field in STEM ( Science, Technology, Engineering, and Math). Lazy is not a derogatory term in this case,it is the desire to find an easy way to do repetitive and mundane tasks. Simplification is also the goal for engineers to help other engineers not to make common mistakes. We can see that trend clearly in loop designs.
First we had while loop that requires Loop Control Variable ( LCV ) priming, boolean logic design, and update to LCV.
java.util.ArrayList<Integer> me=new java.util.ArrayList<>();
me.add(1);
me.add(2);
int count=0; //LCV priming
while(count<me.size()){ //boolean logic
System.out.println(me.get(count));
count++; //update
}
We had a problem since we had to keep track of these three components separately, so in a case of counter controlled loops, a simplified while loop was developed, called for loop. It is a ternary operator using three arguments, all in one line, to simplify the while loop concept.
for(int count=0;count<me.size();count++){
System.out.println(me.get(count1));
}
We still had problems with boolean logic and +/-1 mistakes processing data. So, a simplified version of a for loop was developed, called advanced for loop.
for(int i:me){
System.out.println(i);
}
Hiding the complexities and making things simple is an advanced concept, it is a form of oxymoron in geeks world. Thus, having an input, hiding the complexities of processing the input, and having the desired output is an aim for quicker program development with less logical errors or mistakes. This hiding desire is not a digital age child, it is a mathematical theory and the base for lambda calculus.
Lambda calculus (also written as λ-calculus) is a formal
system in mathematical logic for expressing computation
based on function abstraction and application using variable binding
and substitution. It is a universal model of computation that can be used to
simulate any Turing machine and was first introduced by
mathematician Alonzo Church in the 1930s as part of his research of
the foundations of mathematics.
Lambda calculus consists of constructing lambda terms and performing
reduction operations on them. In the simplest form of lambda calculus, terms
are built using only the following rules:
The Lambda calculus is an abstract mathematical theory of
computation, involving functions. The lambda calculus can be thought of as the
theoretical foundation of functional programming. It is a Turing complete
language; that is to say, any machine which can compute the lambda calculus can
compute everything a Turing machine can (and vice versa).
The notation is based on function abstraction and application based on
variable binding and substitution. If you have done same basic programming
already, you might be familiar with lambda functions or anonymous functions
already. They are featured in Haskell, Mathematica, Python and recent versions
of C++ too. In this wiki, we will mostly stick to Haskell.
This concept of process hiding starts by understanding anonymous classes. As you can see below, class can override methods without changing the original class permanently.
class Perform{
public int doubleIt(int i){
return 2*i;
}
}
public class HiddenProcess{
public static void main(String args[]){
Perform myNumber=new Perform()
{
private String addedAttribute="My Added Message";
public int doubleIt(int i){
return i+i;
}
};
System.out.println(myNumber.doubleIt(3));
}
}
The next concept we need to understand is interfaces. Interfaces can not be instantiated without a class implementing the interface defined method headers.
interface MyWish{
void complete(int a);
}
class MyWishRealize implements MyWish{
public void complete( int a){
System.out.println(3);
}
}
public class HiddenProcess{
public static void main(String args[]){
MyWishRealize wishOne=new MyWishRealize();
}
}
So, we can override and implement methods in anonymous classes therefore we can override methods in classes implementing interfaces.
interface MyWish{
void complete(int a);
}
class MyWishRealize implements MyWish{
public void complete( int a){
System.out.println(3);
}
}
public class HiddenProcess{
public static void main(String args[]){
MyWishRealize wishOne=new MyWishRealize(){
public void complete(int a ){
System.out.println(3000);
}
};
MyWishRealize wishTwo=new MyWishRealize(){
public void complete(int a ){
System.out.println(6000);
}
};
}
}
So, we can also implement an interface using an anonymous class, so we do not need to create an extra class just to implement the interface.
interface MyWish{
void complete(int a);
}
public class HiddenProcess{
public static void main(String args[]){
MyWish gift= new MyWish(){
public void complete( int a){
System.out.println("Hello "+a);
}
};
}
}
But the simplified structure can be even more simplified by lambda expression like this;
interface MyWish{
void complete(int a);
}
public class HiddenProcess{
public static void main(String args[]){
MyWish gift1=(i)->System.out.println("Hello "+i);
}
}
Lambda Expression
Prior to Java 1.8, if you looked at the API for ArrayList, you did not have a method to process the array with forEach mehod.
void
|
Increases the capacity of this ArrayList instance,
if necessary, to ensure that it can hold at least the number of elements
specified by the minimum capacity argument.
|
|
Returns the element at the
specified position in this list.
|
int
|
Returns the index of the first
occurrence of the specified element in this list, or -1 if this list does not
contain the element.
|
Java 1.8 added that method to the available method's list to simplify processing the array.
void
|
Increases the capacity of this ArrayList instance,
if necessary, to ensure that it can hold at least the number of elements
specified by the minimum capacity argument.
|
void
|
Performs the given action for each
element of the Iterable until all elements have been processed or the action
throws an exception.
|
|
Returns the element at the
specified position in this list.
|
int
|
Returns the index of the first
occurrence of the specified element in this list, or -1 if this list does not
contain the element.
|
forEach method is based on the iterable interface and takes a consumer interface as its parameter.·
forEach
public void forEach(Consumer<? super E> action)
Description copied from
interface: Iterable
Performs the given action for each element of the Iterable
until all elements
have been processed or the action throws an exception. Unless otherwise
specified by the implementing class, actions are performed in the order of
iteration (if an iteration order is specified). Exceptions thrown by the action
are relayed to the caller.
Specified
by:
Parameters:
action
- The action to be performed for each element
Iterable inerface's forEach method gives us the reference to the enhanced for loop and to the idea of simplifying its operation.·
forEach
default void forEach(Consumer<? super T> action)
Performs the given action for each element of the Iterable
until all elements
have been processed or the action throws an exception. Unless otherwise
specified by the implementing class, actions are performed in the order of
iteration (if an iteration order is specified). Exceptions thrown by the action
are relayed to the caller.
Implementation
Requirements:
The default implementation behaves as if:
for (T t : this)
action.accept(t);
Parameters:action
- The action to be performed for each element
Since:1.8
The Consumer interface has the method accept that takes any dataType object and performs the given operation on that dataType. Since Consumer is an interface, a class needs to implement its method accept.
java.util.function
Interface Consumer<T>
Type Parameters:
T
- the type of the input to the operation
Functional Interface:This is a functional interface and
can therefore be used as the assignment target for a lambda expression or
method reference.
Modifier
and Type
|
Method
and Description
|
void
|
Performs this operation on the
given argument.
|
class implementConsumer<T> implements java.util.function.Consumer<T> {
public void accept(T i){
System.out.println(i);
}
}
public class HiddenProcess{
public static void main(String args[]){
implementConsumer<String> value=new implementConsumer<>();
value.accept("Hello");
}
}
So, how can we do the same without using the extra class implementConsumer? WE should use anonymous inner class to accomplish that for this interface. But we would need to create an implementation for each dataType and that would be very annoying.
public class HiddenProcess{
public static void main(String args[]){
java.util.function.Consumer<String> value=new java.util.function.Consumer<String>(){
public void accept(String i){
System.out.println(i);
}
};
value.accept("Hello");
}
}
We would need to create an implementation for ArrayList for each dataType it is instantiated and that would annoy us as well.
public class HiddenProcess{
public static void main(String args[]){
java.util.ArrayList<Integer> me=new java.util.ArrayList<>();
me.add(1);
me.add(2);
java.util.function.Consumer<java.util.ArrayList<Integer>> value=new java.util.function.Consumer<java.util.ArrayList<Integer>>(){
public void accept(java.util.ArrayList<Integer> i){
for(int val:i)
System.out.println(val);
}
};
value.accept(me);
}
}
At the end, we can simplify all this with our lambda expression that works for every dataType and the syntax is simplified to a single line of code. Isn't that cool!!!
public class HiddenProcess{
public static void main(String args[]){
java.util.ArrayList<Integer> me=new java.util.ArrayList<>();
me.add(1);
me.add(2);
me.forEach(i->System.out.println(i));
}
}
Conclusion
Being lazy means innovation toward an advanced society, so be proud of it and keep innovating!