Tuesday, August 29, 2023

Functional Programming Explained

Rio: Hello Mr Archie, I have a question for you. I am not able to understand actually what is functional programming? I have always been reading about that while using functional programming we need to forget everything we know about programming. How can we forget about the variable, values, functions, datatypes, indentation, name spaces, hashes, collections, classes, inheritance etc. This statement is quite confusing. Could you please help me understand about the actual idea behind functional programming?

Mr Archie: Hey Rio, that indeed is a very good question. I would try to explain in an easy manner. The above statement should actually be corrected. The functional programming is all about refactoring everything you know about programming.

Let me try to explain what do I mean by both the above statement, Even in a very well designed applications with time, requirement changes or bug fixes need to be implemented/fixed which lead to many changes that leads to re-designing, adding new modules or removing few existing ones. All these factors make the existing design a mess. The subsequent bug fixing will be even harder in such kind of messed up design. There are lots of if else condition introduced many times. This is kind of very generic problem with most of the object oriented programming languages like I can give an example of type erasure process in java which is "Replace all parameters with their bound classes if the parameter is bounded or replace it with Object class if the parameter is unbounded". These kind of if else statements would make one finally feel that the code needs to be refactored by keeping the well designed modules as is  and re-organizing (re-factoring) the badly designed modules and introducing fewer new ones to keep the application outcome same as it was earlier. Now in order to bind the above constructs together in functional programming, we introduce functions as a bridge between them. These functions are written in such a manner that 
  • There are no side effects on the function being called, which means there should be no change in the state of input variables. This can be achieved e.g by using immutable input parameters. e.g say if an x = {3, 4, 5} is applied over a function y which is f(x), at the end of evaluation of y the value of x should still remain {3, 4, 5}. Now a question will arise in your mind that what if the parameters passed are something like array of hash-maps which are mutable. For this we actually always need to make sure that such data structures are immutable. Another question will now come into your mind that if everything is immutable how can we apply logic what needs to be performed. For this, we have to make sure that our data structure should be having an operation to return create a copy of itself which are mutable. To limit the number of copies while creating required mutable data structure, efficient persistent data structures were introduced. These are actually mutable and support copy on modification operation.
  • There are final desired effects produced as per the requirement of the application (enduser). That means we need to implement mutable state now into the application. This can be done by using bridges between the above function with (no side effect) on one side and the business logic on the other which will give the desired result. The function is thrown at the bridge which performs the desired logic. Examples of such bridges are Atomic classes, messaging queues etc which perform the task of producing desired final results.

Here we can think about programming functions just as mathematical function say f(x) which is an nothing but an abstract relationship between input set and output set. These will always give us same output for the same set of input. 

Rio: Why do we call them persistent data structure? It does not seem to be having any relation to database here.

Mr Archie: Yeh actually it was unfortunate by the designers of functional programming paradigm to use the term persistent here. Actually they are called efficient persistent data structures as they always store the previous version/copy of themselves when modified. Even if they are storing every copy while applying any modification,  they are written so efficiently that the minimum copies are created via using data structures like trees/graphs etc using branching factor of 32 which can support millions of elements. Only the node where modification is done is copied, the rest nodes are still pointing to the original data structure.

Rio: What is the biggest benefit of using functional programming?

Mr Archie:  Sure, few of the benefits achieved by using functional programming approach are as below
  • There are no assignments statements in functional programs, so the variables are immutable
  • We don't need to worry much about multithreading as the input data is immutable.
  • Since there are no side effects, so there is very less chance of introducing bugs.
  • Since the functions are referentially transparent, which means independent of each other, so the order of the program statements need not to be imperative.

Rio: And what are first class functions in programming language?

Mr Archie: A function that can take another function as a parameter or can even return a function as an output is called a first class function.

Rio: If I ask you specifically about how do we use functional programming in Java.

Mr Archie: The ways with which we use functional programming in java are:
  • Using Anonymous inner classes where we can create and instantiate a class at the same time. This is used if we want to use class declaration just once. 
  • Using Lambda Expression or anonymous functions which can accept parameters and return a value.
  • Using Method References.
Rio: The concept of Method reference was introduced in java 8, if I am not wrong. Can you please elaborate a bit how we can used method references in functional programming?
 
Mr Archie: Off-course, why not. Method references are nothing but a special type of lambda expression. They are called method references as they refer to an existing method. They can be used in 4 ways. I will explain all one by one

  • Referring static method
                    The String Utils class a static method capitalize(String string) which converts given string to all capital cases. This can be achieved as below
public class MethodReferenceForStaticMethods {
public static void main(String[] args) {
List<String> list = List.of("rio", "mr. achie");
list.stream().forEach(word -> {
StringUtils.toRootUpperCase(word);
});
}
}

           The same result can also be achieved using method references as below

public class MethodReferenceForStaticMethods {
public static void main(String[] args) {
List<String> list = List.of("rio", "mr. achie");
list.forEach(StringUtils::toRootUpperCase);
}
}
  • Referring instance method
                        If an instance method of a class is having same number of arguments as present in a functional interface, the instance method can be referred as below

public class MethodReferenceForInstanceMethods {
public static void main(String[] args) {
MethodReferenceForInstanceMethods instance =
new MethodReferenceForInstanceMethods();
MyFunctionalInterface methodReferenceForInstanceMethods =
instance::instanceMethod;
methodReferenceForInstanceMethods.say("a", "b", 5, 8);
}

public void instanceMethod(
String a, String b, int c, Object d
) {
System.out.println("Hello, this is non-static method by: "
+ a + " " + b + " " + c + " " + d);
}

interface MyFunctionalInterface {
void say(String a, String b, int d, Object e);
}
}

Rio: Thank you so much Mr Archie for making me understand the paradigm of functional programming in such a simple way. I will get back to you with more queries about it in coming days.

Mr Archie No issues, you can come over to me at any time. 




Sunday, August 27, 2023

Generics in Java

Rio:  Guys, today Mr Archie would be telling us about the concept of Generics in Java. I am sure this lecture is going to clear all your doubts around Generics. You may come up with your queries as we move along. You have the floor now, Mr Archie.

Mr Archie: Thank you, Rio. So guys, most of you must have already heard about the term Generics in Java.


Rio: Yes, Mr Archie.


Mr Archie: Good, so have you ever wondered where do we use Generics in Java?


Audience is silent.


Mr Archie: Generics help us in implementing generic code which can work with different data types. They help us in type checking of the java objects at the compile time rather than facing any casting related issues at run time. Let me try to explain this using an example here.

Suppose you want to create a class to store and print a string. so you would be doing something like this and you can create an object of this class and call print method as below.
class StringPrinter {
private String toPrint;
StringPrinter(String toPrint){
this.toPrint = toPrint;
}
public void print(){
System.out.println(toPrint);
}
}
public class Generics {
public static void main(String[] args) {
StringPrinter printer = new StringPrinter("Anshul");
printer.print();
}
}

Suppose later at some point of time you need to create another class which you should be able to use to print an integer. You can create another class almost like the above one

public class IntegerPrinter {
private Integer toPrint;
IntegerPrinter(Integer toPrint) {
this.toPrint = toPrint;
}
public void print() {
System.out.println(toPrint);
}
}
public class Generics {
public static void main(String[] args) {
StringPrinter stringPrinter = new StringPrinter("Anshul");
stringPrinter.print();
IntegerPrinter integerPrinter = new IntegerPrinter(12);
integerPrinter.print();
}
}

If you take a look carefully, the code is almost duplicate for the two classes viz. StringPrinter and IntegerPrinter.


This duplicity can easily be removed using Generics. We can create a parameterised class as below

public class Printer<T> {
T toPrint;
Printer(T toPrint){
this.toPrint = toPrint;
}
public void print(){
System.out.println(toPrint);
}
}
public class Generics {
public static void main(String[] args) {
Printer<String> stringPrinter = new Printer<>("Anshul");
stringPrinter.print();
Printer<Integer> integerPrinter = new Printer<>(12);
integerPrinter.print();
} 

Jamie (among audience): Wao thats cool!.

Mr Archie: Yes its, indeed, a cool feature provided by Java. Not only classes we can also create generic methods using Generics.
public class Printer<T> {
T toPrint;
Printer(T toPrint){
this.toPrint = toPrint;
}
public void print(){
System.out.println(toPrint);
}
public T get(){
return toPrint;
}
}

If you would notice carefully you would see that the method get is returning a generic Type and we can call this method to get the respective datatypes

public class Generics {
public static void main(String[] args) {
Printer<String> stringPrinter = new Printer<>("Anshul");
String str = stringPrinter.get();
System.out.println(str);
Printer<Integer> integerPrinter = new Printer<>(12);
Integer integer = integerPrinter.get();
System.out.println(integer);
}
}
Rio: Can we use primitive data types in generics?
Mr Archie: Generics work with Reference Types only. You can not use primitive data types.

Rio: Thanks!

Jamie: Mr Archie, I had once heard about bounded and Unbounded Generic Types. Can you please explain those concepts, as well, here?

Mr Archie: Yes Jamie, I was about to explain those. In the above example the Type parameter can take any argument. So this will be called an unbounded generic type. In all we can categorise generics into 2 different types
  • Unbounded Generic Types: The unbounded generic types can take any known reference type <T> or a wildcard <?> if the type is unknown. 
  • Bounded Generic Types: In case, for known reference types, if you want to create a boundary of  the types that will be  accepted by the generic class/method. we can use bounded generic types.
Rio: So you mean, if we want to restrict the types that will be accepted by generic class/methods we can do that? So how is that possible?

Mr Archie: Let me explain you in better way. Till this time, all the examples, I gave above, were using unbounded or simple generic type where in we were not limiting the types that could be used with the Generic class. We could use any reference type with the above generic class e.g. String, Integer, Animal, Airplane, Student, any. There may come scenarios where you might need to restrict your generic class to accept only limited reference types. Bounded Generic Types help us to achieve this requirement. They are further of 2 types
  • Upper Bounded
  • Lower Bounded
Jamie: Examples Mr Jamie, please?

Mr Archie: Suppose you want to make your generic class accept only type T and its subtypes, you will go for Upper Bounded Generic Type. Let me explain it using the above example only.

The generic class Printer above, can accept any reference type, even say an Animal class, if exists.
public class Animal {
private String name;
Animal(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
'}';
}
}
public class Generics {
public static void main(String[] args) {
Printer<String> stringPrinter = new Printer<>("Anshul");
String str = stringPrinter.get();
System.out.println(str);
Printer<Integer> integerPrinter = new Printer<>(12);
Integer integer = integerPrinter.get();
System.out.println(integer);
Printer<Animal> aPrinter = new Printer<>(new Animal("Cat"));
System.out.println(aPrinter);
}

}

If we want this Printer class to accept Number or its subtypes only, we can use Upper Bounded Generic via extends keyword as below
public class Printer<T extends Number> {
T toPrint;
Printer(T toPrint){
this.toPrint = toPrint;
}
public void print(){
System.out.println(toPrint);
}
public T get(){
return toPrint;
}

public String toString(){
return toPrint.toString();
}
After doing so, the Printer class would not be able to accept either String or Animal type, and you will get compile time error saying "Type parameter is not within bounded" while trying to do so.

and similarly if we want this Printer class to accept Animals or its subtypes only, we can change it to
public class Printer<T extends Animal> {
T toPrint;
Printer(T toPrint){
this.toPrint = toPrint;
}
public void print(){
System.out.println(toPrint);
}
public T get(){
return toPrint;
}
public String toString(){
return toPrint.toString();
}
}
Lower Bounded Generic Type parameter is, however, not supported at class level but rather at method level only. This is because wildcard ? can not be used at class level. It makes no sense using it at the class level. Let me try to explain the above statements.

If we could have, anyhow, been able to create a class Printer with ? type parameter, we would have been in, no way, able to determine the type to be used for declaring instance variable for that or using them inside any method. With classes you need some identifier unlike that with methods where you don't. This is quite confusing for now. I will try to cover this topic in some other lecture. For now just remember that we can not use super keyword in type parameter while declaring a generic class.
Rio: Sure, no worries. We will take a note of this.

Mr Archie: Now for using super keyword in case of lower bounded generics, let me try to give an example here. Suppose we have two different lists, one of Number type and another of integer type. We want to create a generic method that works on both of them, if we try to do this way
public class Generics {
public static void main(String[] args) {
List<Number> listNumber =Arrays.asList(1, 2);
List<Integer> listInteger =Arrays.asList(1, 2);
List<Object> listObject =Arrays.asList(1, 2);
print(listNumber);
print(listInteger);
print(listObject);
}
private static void print(List<Integer> listNumber) {

}
private static void print(List<Number> listNumber) {

}
private static void print(List<Object> listNumber) {

}
}
We will get a compile time error saying "both methods have same erasure" as Integer already extends Number class in java. How can we write generic method that supports both Integer and its super classes. We can in this case use lower bounded generics. This required generic method can be written as
public class Generics {
public static void main(String[] args) {
List<Number> listNumber =Arrays.asList(1, 2);
List<Integer> listInteger =Arrays.asList(1, 2);
List<Object> listObject =Arrays.asList(1, 2);
print(listNumber);
print(listInteger);
print(listObject);
}
private static void print(List<? super Integer> listNumber) {
listNumber.stream().forEach(System.out::println);
}
}
Rio: Mr Archie, so how can we determine where we should use upper bound generics and where we should go for lower bound generics?

Mr Archie: A very simple get-put rule if we can to get something out, go for Upper Bound and if we want to set something in, go for Lower Bound.

One more topic I think can be covered here today, is about variance.

Rio: Oh still more left in there. Please go ahead, quite interesting.

Mr Archie: Variance is the assignment compatibility between generic classes and methods.

Lets take an example, we have an array of animals and a list of animals. We have two methods one with Object[] as parameter and another with List<Object> as parameter.
public class Generics {
public static void main(String[] args) {
Animal[] arrayAnimal = new Animal[10];
arrayAnimal[0] = new Animal("monkey");
arrayAnimal[1] = new Cat("cat");
arrayAnimal[2] = new Dog("dog");
List<Animal> listAnimal = new ArrayList<>();
listAnimal.add(new Animal("monkey"));
listAnimal.add(new Cat("cat"));
listAnimal.add(new Dog("dog"));
}
private static void doStuff(Object[] objects) {
System.out.println(objects);
}
private static void doStuff(List<Object> objects) {
System.out.println(objects);
}
}
If we try to pass the arrayAnimal to method accepting Object[] this is acceptable but if we try to pass listAnimal to method accepting List<Object>, we get compile time error. This is because arrays of animal is subtype of array of object but list of animal is not a subtype of list of object. This is called invariant method.

To fix this issue, we can change the method to use wildcard ? to allow list of any type and this will make the method bi-variant.
public class Generics {
public static void main(String[] args) {
List<Animal> listAnimal = new ArrayList<>();
listAnimal.add(new Animal("monkey"));
listAnimal.add(new Cat("cat"));
listAnimal.add(new Dog("dog"));
doStuff(listAnimal);
}
private static void doStuff(List<?> objects) {
System.out.println(objects);
}
}
Another method to fix this issue, we can change the method a bit to allow objects of  type T (Animal in this case) and objects of subtypes of T to make it covariant. This is nothing but using concept of upper bound generics.
public class Generics {
public static void main(String[] args) {
Animal[] arrayAnimal = new Animal[10];
arrayAnimal[0] = new Animal("monkey");
arrayAnimal[1] = new Cat("cat");
arrayAnimal[2] = new Dog("dog");
List<Animal> listAnimal = new ArrayList<>();
listAnimal.add(new Animal("monkey"));
listAnimal.add(new Cat("cat"));
listAnimal.add(new Dog("dog"));
doStuff(arrayAnimal);
doStuff(listAnimal);
}
private static void doStuff(Object[] objects) {
System.out.println(objects);
}
private static void doStuff(List<? extends Animal> objects) {
System.out.println(objects);
}
}
Similarly for understanding contra-variance, lets take an example of scenario used above
public class Generics {
public static void main(String[] args) {
List<Number> listNumber =Arrays.asList(1, 2);
List<Integer> listInteger =Arrays.asList(1, 2);
List<Object> listObject =Arrays.asList(1, 2);
print(listNumber);
print(listInteger);
print(listObject);
}
private static void print(List<? super Integer> listNumber) {
listNumber.stream().forEach(System.out::println);
}
Here the print method has bound the input parameters to be of type T (Integer in this case) and its superclasses viz. Number as well as Object to make it contra-variant.

Rio: Wao, very nicely explained!

Mr Archie: One more thing that just came into my mind is Type Erasure Process. This is in-fact the process by which a compiler replaces all the generic parameters in generic class with actual ones. Compiler follows these rules in this process
  • For bounded type parameters, bounded types are inserted.
  • For unbounded type parameters, Object class is inserted.
  • To preserve type safety, type casts are introduced t
  • To preserve polymorphism in extended generic type classes, bridge methods are generated.
Thats all for today guys. We will meet soon again to discuss some other interesting topic. Thank you everyone.

Rio: Thanks, Mr Archie for this wonderful session today. This indeed helped us in understanding the concept of Generics in Java. See you soon in another session.


SpringBoot Application Event Listeners

When a spring boot application starts few events occurs in below order ApplicationStartingEvent ApplicationEnvironmentPreparedEvent Applicat...