January 29, 2023

Java Generics - Type Erasure

Using Java generics you can write generic programs and it also provide tighter type checks at compile time but these generics type remain only at source code level. When the source code is compiled all the generic type parameters are erased and this process is called type erasure in Java Generics.

How does Type erasure work

Type erasure in Java works as follows-

  1. Replace all type parameters in generic types with their bound type, if no explicit bound type is specified then replace generic type parameter with Object. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods with all the generic parameters replaced with actual types.
  2. Insert type casts if necessary to preserve type safety.
  3. Generate bridge methods to preserve polymorphism in extended generic types.

Type erasure in Generic class

Consider the following generic class with a generic type parameter T.

public class GenericClass<T> {
  T obj;
  GenericClass(T obj){
    this.obj = obj;
  }
  public T getObj() {
    return obj;
  } 
}

Because the type parameter T is unbounded, the Java compiler replaces it with Object and after compilation class looks like-

public class GenericClass {
  Object obj;
  GenericClass(Object obj){
    this.obj = obj;
  }
  public Object getObj() {
    return obj;
  } 
}

Consider another generic class with a bounded type parameter.

public class GenericClass<T extends String> {
  T obj;
  GenericClass(T obj){
    this.obj = obj;
  }
  public T getObj() {
    return obj;
  } 
}

Because the type parameter T is bounded, the Java compiler replaces it with the bound class String and after compilation class looks like-

public class GenericClass {
  String obj;
  GenericClass(String obj){
    this.obj = obj;
  }
  public String getObj() {
    return obj;
  } 
}

Type erasure in Generic method

The Java compiler also erases type parameters in generic method arguments. Consider the following generic method which counts the number of occurrences of passed element in the passed array.

public static <T> int count(T[] numberArray, T elem) {
  int cnt = 0;
  for (T e : numberArray){
    if (e.equals(elem))
      ++cnt;
  }
  return cnt;
}

Because T is unbounded, the Java compiler replaces it with Object and the compiled method looks like-

public static int count(Object[] numberArray, Object elem) {
  int cnt = 0;
  for (Object e : numberArray){
    if (e.equals(elem))
      ++cnt;
  }
  return cnt;
}

Type erasure and bridge methods

Sometimes, as part of the type erasure process compiler creates a synthetic method, called a bridge method. Consider the following classes to see an example of bridge method in Java.

public class GenClass<T> {
  T obj;
  public GenClass(T obj) { 
    this.obj = obj; 
  }
  public void setObj(T obj) {
    this.obj = obj;
  }  
}

This GenClass is then extended by another class as given below-

public class MyClass extends GenClass {
  public MyClass(Integer data) { 
    super(data); 
  } 
  public void setObj(Integer data) {
    System.out.println("MyClass.setData");
    super.setObj(data);
  } 
}

The intention here is to override the parent class setObj() method in the subclass. After type erasure, the GenClass and MyClass classes become-

public class GenClass {
  Object obj;
  public GenClass(Object obj) { 
    this.obj = obj; 
  }
  public void setObj(Object obj) {
    this.obj = obj;
  }  
}
public class MyClass extends GenClass {
  public MyClass(Integer data) { 
    super(data); 
  } 
  public void setObj(Integer data) {
    System.out.println("MyClass.setData");
    super.setObj(data);
  } 
}

After type erasure, the method signatures do not match. The GenClass method becomes setObj(Object obj) and the MyClass method becomes setObj(Integer data). Therefore, the GenClass setObj method does not override the MyClass setObj method.

To solve this problem and preserve the polymorphism of generic types after type erasure, Java compiler generates a bridge method to ensure that subtyping works as expected. For the MyClass class, the compiler generates the following bridge method for setObj().

public class MyClass extends GenClass {
  public MyClass(Integer data) { 
    super(data); 
  } 
  // Bridge method generated by the compiler
  public void setObj(Object data) {
    setObj((Integer) data);
  }
  public void setObj(Integer data) {
    System.out.println("MyClass.setData");
    super.setObj(data);
  } 
}

As you can see the bridge method has the same method signature as the GenClass’ setObj() method and it delegates to the original setObj() method.

That's all for the topic Java Generics - Type Erasure. If something is missing or you have something to share about the topic please write a comment.


You may also like

January 28, 2023

Heap Sort Java Program

This tutorial shows how to write Heap sort program in Java which is an in-place sorting algorithm. Heap sort uses heap data structure for sorting the elements so the obvious question is what is heap?

Heap data structure

A heap is a binary tree so each node can have maximum two children and it has following properties-

  1. It is a complete binary tree which means reading from left to right it is completely filled in (all the nodes have 2 children) except the last row which need not be full.
  2. Each node in the heap satisfies the condition that each node is greater than or equal to its child nodes in case of Max heap. Node is less than or equal to its child nodes in case of Min heap.
Heap data structure

Heap sort algorithm

Steps for writing the Heap Sort Java program are as follows-

  1. Create a max heap from input array. Using max heap sorting will be done in ascending order. For descending order you can use min heap. Heap data structure is also represented using array. This process of creating heap from the input array is called heapify.
  2. Once the heap is created its root node is the maximum element. Swap the root element with the last element of the array.
  3. This swapping disturbs the heap so structure has to be heapified again using the array. This time last element is excluded (array length decreased by one) as it is already at its final place.
  4. Repeat step 2 and 3 until sorting is complete.

How to create heap from array

Creating heap from an array is an important part of heap sort so understanding it is important.

Array is considered as a complete binary tree with each element considered as a node. With in an array for each node you can get its parent node, left child node and right child node using the following equations-

For a node at index i in the array-

  • Parent node is– (i-1)/2
  • Left child node is- 2*i + 1
  • Right child node is- 2*i+2

To create a heap you'll have to start from the nodes at the bottom and move upwards comparing if child node is greater than the parent and swapping node values if that is true. Since last level has leaf nodes (nodes with no children) so this comparison has to be started from one level above.

For an array of length n, last node will be at index (n-1) thus the index of its parent node should be (n-1)/2 using the equation. Heapifying the array starts from this parent node, in each iteration compare the parent node with left child and right child and swap the nodes if child is greater than the parent.

For example if input array is [5, 12, 3, 16, 8, 10] then the complete binary tree for this array can be visually represented as-

Binary tree

Since last index is 5 thus the parent node should be at index (5-1)/2 = 2. Process of creating a heap starts from that index 2. Compare node at index 2 with its child nodes and swap if any of the child is greater than the parent node. In our tree 10 > 3 so these values are swapped. When index is 1, node at index 1 is compared with its child nodes and values swapped if required.

heap sort

In next iteration comparison and swapping is done for index 0.

Heap Sort Java

Heap Sort Java program

public class HeapSort {

  public static void main(String[] args) {
    int[] arr = {5, 12, 3, 16, 8, 10};	
    System.out.println("Original array- " + Arrays.toString(arr));
    HeapSort hs = new HeapSort();
    hs.heapSort(arr);
    System.out.println("Sorted array after heap sort- " + Arrays.toString(arr));
  }
	
  private void heapSort(int[] arr){
    int arrLength = arr.length;
    // create heap from array start from index (n-1)/2
    for(int i = (arrLength-1)/2; i >= 0; i--){
      heapify(arr, arrLength, i);
    }
    System.out.println("heapified array- " + Arrays.toString(arr));
    // Heap Sort 
    for(int i = arrLength-1; i >= 0; i--){
      // Swap root and last nodes 
      swap(arr, i, 0);
      // Reconstruct heap again 
      heapify(arr, i, 0);
    }
  }
    
  private void heapify(int[] numArr, int index, int i){
    // Getting parent and children indexes
    int root = i;
    int leftChild = 2*i + 1;
    int righChild = 2*i + 2;
    //compare left child value
    if(leftChild < index && numArr[leftChild] > numArr[root])
      root = leftChild;
    //comparing right child value
    if(righChild < index && numArr[righChild] > numArr[root])
      root = righChild;
      // swap values if required and call method recursively for next level
      if(root != i){
        swap(numArr, root, i);
        heapify(numArr, index, root);
      }
    }
    
    private void swap(int[] numArr, int index, int li){
      int temp = numArr[li];
      numArr[li] = numArr[index];
      numArr[index] = temp;
    }
}

Heap sort time and space complexity

Time required to do any common tree operation is O(logn). For Heap sort creation of heap is done for n elements thus the time complexity of Heap sort is O(n*logn). This time complexity remains the same however the data is distributed. That’s where Heap sort scores over Quick sort which is another O(n*logn) sorting algorithm. In worst case Quick sort may become O(n2) but Heap sort is always O(n*logn).

Since same array is used for arranging the elements in order so no extra space requirement is there. Thus the space complexity of Heap sort is O(1).

That's all for the topic Heap Sort Java Program. If something is missing or you have something to share about the topic please write a comment.


You may also like

January 27, 2023

Java TemporalAdjusters Class With Examples

The java.time.temporal.TemporalAdjusters class is part of new date and time API added in Java 8 which provides common and useful TemporalAdjusters. TemporalAdjusters class in Java has many static methods for modifying temporal objects i.e. methods for adjustments of date and time.

You have to understand two interfaces Temporal and TemporalAdjuster to understand the whole concept of temporal objects.

java.time.temporal.Temporal interface

Temporal is a framework-level interface defining read-write access to a temporal object, such as a date, time, offset or some combination of these.

This is the base interface type for date, time and offset objects that are complete enough to be manipulated using plus and minus thus it provides access to date/time/offset information.

Some of the classes in new date and time API that implements Temporal interface are given below-

  • Instant
  • LocalDate
  • LocalDateTime
  • LocalTime
  • OffsetDateTime
  • OffsetTime
  • ZonedDateTime

So objects of these classes are of type Temporal and static methods of TemporalAdjusters class can modify these temporal objects.

java.time.temporal.TemporalAdjuster interface

TemporalAdjuster is a functional interface providing strategy for adjusting a temporal object. This interface has a single abstract method adjustInto() which adjusts the specified temporal object.

For example-

temporal = thisAdjuster.adjustInto(temporal);

But the recommended way is to use temporal = temporal.with(thisAdjuster); method rather than directly using the adjustInto() method.

java.time.temporal.TemporalAdjusters class

TemporalAdjusters class has many convenient methods that return a TemporalAdjuster. Some of the methods are listed below-

  • TemporalAdjuster dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek)- Returns the day-of-week in month adjuster, which returns a new date with the ordinal day-of-week based on the month.
  • TemporalAdjuster firstDayOfMonth()- Returns the "first day of month" adjuster, which returns a new date set to the first day of the current month.
  • TemporalAdjuster firstDayOfNextMonth()- Returns the "first day of next month" adjuster, which returns a new date set to the first day of the next month.
  • TemporalAdjuster firstDayOfYear()- Returns the "first day of year" adjuster, which returns a new date set to the first day of the current year.
  • TemporalAdjuster lastDayOfMonth()- Returns the "last day of month" adjuster, which returns a new date set to the last day of the current month.
  • TemporalAdjuster lastDayOfYear()- Returns the "last day of year" adjuster, which returns a new date set to the last day of the current year.

Java TemporalAdjusters Examples

1. Finding the first or last day of the month for a LocalDateTime object.

LocalDateTime ldt = LocalDateTime.now();
System.out.println("Current date time- " + ldt);
System.out.println("First day of the month- " + ldt.with(TemporalAdjusters.firstDayOfMonth()));
System.out.println("Last day of the month - " + ldt.with(TemporalAdjusters.lastDayOfMonth()));
Output
Current date time- 2019-11-20T10:39:26.583287900
First day of the month- 2019-11-01T10:39:26.583287900
Last day of the month – 2019-11-30T10:39:26.583287900

2. Finding the day of week in the month for a LocalDate object. In the example code first Sunday of the given month and the previous Sunday is calculated.

LocalDate ld = LocalDate.now();
System.out.println("Current Date - " + ld);
System.out.println("First Sunday of the month - " + ld.with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.SUNDAY)));
System.out.println("Previous Sunday - " + ld.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY)));
Output
Current Date - 2019-11-20
First Sunday of the month - 2019-11-03
Previous Sunday – 2019-11-17

Custom TemporalAdjuster examples

You can implement your own TemporalAdjuster to cater to specific requirement. Suppose you want to get the last working day of the month.

import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;

public class WorkingDayAdjuster implements TemporalAdjuster {
  @Override
  public Temporal adjustInto(Temporal temporal) {
    LocalDateTime ldt = LocalDateTime.from(temporal);
    ldt = ldt.with(TemporalAdjusters.lastDayOfMonth());
    if(ldt.getDayOfWeek() == DayOfWeek.SATURDAY || ldt.getDayOfWeek() == DayOfWeek.SUNDAY) {
      ldt = ldt.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
    }
    return temporal.with(ldt);
  }
}
public class WorkingDay {
  public static void main(String[] args) {
    LocalDateTime ldt = LocalDateTime.now();
    System.out.println("Current Date - " + ldt);
    System.out.println("Last working day of the month - " + ldt.with(new WorkingDayAdjuster()));
  }
}
Output
Current Date - 2019-11-20T11:10:48.365786300
Last working day of the month - 2019-11-29T11:10:48.365786300

That's all for the topic Java TemporalAdjusters Class With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

January 26, 2023

Java Reflection – Class Fields

Using Java Reflection you can get information about the class fields and get and set field values at run time. In Java Reflection API there is a class java.lang.reflect.Field that has methods for accessing field's type, field’s modifier and setting and getting values of a field.

Getting Field instance

First thing is to get the instance of Field class for that you will have to use methods of the java.lang.Class as that class is the entry point for all reflection operations.

There are 4 methods for getting field instance-

  1. getField(String name)- Returns a Field object representing the public member field of the class or interface.
  2. getFields()- Returns an array containing Field objects reflecting all the accessible public fields of the class or interface.
  3. getDeclaredField(String name)- Returns a Field object that reflects the specified declared field (even private) of the class or interface represented by this Class object.
  4. getDeclaredFields()- Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this Class object. This includes public, protected, default (package) access, and private fields, but excludes inherited fields.

Getting information about class fields – Java example

This example shows how to get information like type, modifier of individual/all fields of a class. Methods of the Field class used are-

  • getType() method of the Field class returns the declared type for the field.
  • getModifiers() method of the field class returns the modifiers for the field as an integer.
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Set;

public class FieldReflectionDemo {
  public String str = "Reflection";
  private int i;
  public boolean flag = false;
  public Set<String> citySet;
    
  public static void main(String[] args) {
    try {
      FieldReflectionDemo obj = new FieldReflectionDemo();
      Class<?> c = obj.getClass();
      // get specific field
      Field f = c.getField("str");
      // getting field type
      Class<?> type = f.getType();
      // getting field modifiers
      int mod = f.getModifiers();
      System.out.println("Field name - " + f.getName()); 
      System.out.println("Field type - " + type.getName());
      System.out.println("Field modifiers - " + Modifier.toString(mod));
      System.out.println("--- Getting all fields ---");
      Field[] fields = c.getDeclaredFields();
      for(Field field : fields) {
        System.out.println("Field name - " + field.getName()); 
        System.out.println("Field type - " + field.getType());
        System.out.println("Field modifiers - " + Modifier.toString(field.getModifiers()));
      }        
    } catch (NoSuchFieldException | SecurityException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
Output
Field name - str
Field type - java.lang.String
Field modifiers - public
--- Getting all fields ---
Field name - str
Field type - class java.lang.String
Field modifiers - public
Field name - i
Field type - int
Field modifiers - private
Field name - flag
Field type - boolean
Field modifiers - public
Field name - citySet
Field type - interface java.util.Set
Field modifiers - public

Getting and setting values

For getting value there is get(Object obj) method which returns the value of the field represented by this Field, on the specified object.

For Setting value there is set(Object obj, Object value) method which sets the field represented by this Field object on the specified object argument to the specified new value.

import java.lang.reflect.Field;
import java.util.Set;

public class FieldReflectionDemo {
  public String str = "Reflection";
  private int i;
  public boolean flag = false;
  public Set<String> citySet;
    
  public static void main(String[] args) {
    try {
      FieldReflectionDemo obj = new FieldReflectionDemo();
      Class<?> c = obj.getClass();
      // get specific field
      Field f = c.getField("str");
      // show value - get method
      System.out.println("Field name- " + f.getName() + " Value- " + f.get(obj));
      // set new value
      f.set(obj, "New Value");
      System.out.println("Field name- " + f.getName() + " Value- " + f.get(obj));        
    } catch (IllegalAccessException | NoSuchFieldException | SecurityException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
Output
Field name- str Value- Reflection
Field name- str Value- New Value

Getting and setting value of a private field

You can even set value of a private field using Reflection. For that you need to set the accessible as true using setAccesssible() method.

In the example we have a class Access with one private int field i. In FieldReflectionDemo class we’ll access this field using reflection and set a value to it. If you don’t set the accessible flag using setAccesssible() method it will throw IllegalAccessException as shown below.

class Access{
  private int i;
}

public class FieldReflectionDemo {
  public String str = "Reflection";
  public boolean flag = false;
  public Set<String> citySet;
    
  public static void main(String[] args) {
    try {
      Access obj = new Access();
      Class<?> c = obj.getClass();
      // get specific field
      Field f = c.getDeclaredField("i");
      // show value - get method
      System.out.println("Field name- " + f.getName() + " Value- " + f.get(obj));
      // set new value
      f.set(obj, 7);
      System.out.println("Field name- " + f.getName() + " Value- " + f.get(obj));       
    } catch (IllegalAccessException | NoSuchFieldException | SecurityException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
Output
java.lang.IllegalAccessException: class com.knpcode.programs.FieldReflectionDemo cannot access a member of class com.knpcode.programs.Access with modifiers "private"

Run again after making this change and you will be able to access even the private fields.

Field f = c.getDeclaredField("i");
f.setAccessible(true);
Output
Field name- i Value- 0
Field name- i Value- 7

Reference: https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/reflect/Field.html

That's all for the topic Java Reflection – Class Fields. If something is missing or you have something to share about the topic please write a comment.


You may also like

January 25, 2023

Java Generics- Generic Class, Interface And Method

Generics in Java was introduced in Java 5 to provide strict type checking at compile time.

Type parameters in Java Generics

Generics in Java enables you to write generic classes, interfaces and methods that can work with different data types. It is possible because you specify type parameters when defining classes, interfaces and methods. Type parameter can be any class or interface like Integer, String, custom class or interface.

For example in Collection API ArrayList class is written as-

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{

..
..
}

Here E is a type parameter which is enclosed in angle brackets (<>). Since a type parameter is specified- ArrayList<E> that means ArrayList class is a generic class where E is the type of elements in this List. With this generic class definition ArrayList can work with different data types. You will have to specify the actual type while initializing the ArrayList.

//List that stores Integers
List<Integer> nList = new ArrayList<Integer>();
nList.add(1);
nList.add(2);
nList.add(3);

// List that stores Strings
List<String> sList = new ArrayList<String>();
sList.add("A");
sList.add("B");
sList.add("C");

// List that stores objects of type Employee
List<Employee> eList = new ArrayList<Employee>();
Employee emp1 = new Employee("Jean", "HR", 6000);
eList.add(emp1);

Why Generics is required

You may argue that there is already an Object class in Java that can be used to refer any class object making it a good candidate to be used as a generic parameter. For example in the following class there is a method that has Object type as a parameter so you can pass any type to this method.

public class Test {
  public static void main(String[] args) throws IOException {
    Test t = new Test();
    t.display(1);
    t.display("Hello");
    t.display(5.67);
  }

  public void display(Object o) {
    System.out.println("passed argument is- " + o);
    System.out.println("passed argument's type is- " + o.getClass().getTypeName());
  }
}
Output
passed argument is- 1
passed argument's type is- java.lang.Integer
passed argument is- Hello
passed argument's type is- java.lang.String
passed argument is- 5.67
passed argument's type is- java.lang.Double

As you can see by using Object as parameter I can have a generic method that can work with any type so why Generics is required. Answer is Generics in Java bring type safety to your code. We’ll discuss that feature in next section.

Benefits of Java Generics

1. Strict type checks at compile time

Generics provide strict type checks at compile time so any type violation will give error as compile time itself rather than java.lang.ClassCastException thrown at run time.

For example you have initialized a List as a non-generic List and your intention is to store strings in it. Since it is not Generic which means all its elements will be stored as objects of Object class. If you add an Integer to this List by mistake there won't be any compile time error as Integer is also of type Object.

At the time of retrieving element from the List you will have to explicitly cast it to the type and that time it will throw ClassCastException when it encounters Integer.

public class Test {
  public static void main(String[] args) throws IOException {
    // Not generic
    List sList = new ArrayList();
    sList.add("A");
    sList.add("B");
    // Adding Integer
    sList.add(1);
    sList.add("C");
    
    Iterator itr = sList.iterator();
    while(itr.hasNext()){
      // Casting to string when retrieving
      String str = (String)itr.next();
      System.out.println("" + str);
    }
  }
}
Output
A
B
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
	at com.knpcode.programs.Test.main(Test.java:27)

With Generics you can specify the type of elements that can be stored in the List thus providing type safety. If you try to add element of any other type to this List you will get error at compile time itself.

public class Test {
  public static void main(String[] args) throws IOException {
    // Generic List
    List<String> sList = new ArrayList<String>();
    sList.add("A");
    sList.add("B");
    // Not allowed, Error at compile time if 
    // Integer is added 
    //sList.add(1);
    sList.add("C");
    
    Iterator<String> itr = sList.iterator();
    while(itr.hasNext()){
      String str = itr.next();
      System.out.println("" + str);
    }
  }
}

2. Explicit casting not required

Since type is specified with Generics and it is ensured that you can store elements only of the specified type so explicit casting is not required when retrieving elements.

In the above code when using a non-generic List, type casting is required.

Iterator itr = sList.iterator();
while(itr.hasNext()){
  // Casting to string when retrieving
  String str = (String)itr.next();
  System.out.println("" + str);
}

With a generic List type casting is not required.

Iterator<String> itr = sList.iterator();
while(itr.hasNext()){
  String str = itr.next();
  System.out.println("" + str);
}

3. Implement generic algorithms

By using generics, programmers can implement generic algorithms that work on different types, easier to read and are type safe too. Here is a simple Generic class example that can set and get value of any type.

public class Test<T> {
  T obj;
  public T getObj() {
    return obj;
  }
  public void setObj(T obj) {
    this.obj = obj;
  } 

  public static void main(String[] args) throws IOException {
    // With Integer type
    Test<Integer> intParam = new Test<Integer>();
    intParam.setObj(7);
    int value = intParam.getObj();
    System.out.println("Integer value- " + value);
    
    // With String type
    Test<String> strParam = new Test<String>();
    strParam.setObj("Test Value");
    String str = strParam.getObj();
    System.out.println("String value- " + str);
    
    // With Double type
    Test<Double> doubleParam = new Test<Double>();
    doubleParam.setObj(23.45);
    double dblValue = doubleParam.getObj();
    System.out.println("Double value- " + dblValue);
  }
}
Integer value- 7
String value- Test Value
Double value- 23.45

See How to write a generic Bubble Sort in Java in this post- Generic Bubble Sort Java Program

Type Parameter Naming Conventions in Java Generics

By convention, type parameter names are single, uppercase letters. The most commonly used type parameter names are:

  • T - Type
  • V - Value
  • E - Element
  • K - Key
  • N - Number
  • S,U,V etc. - 2nd, 3rd, 4th types

Generic Class

With the introduction of Generics done let’s see how we can create a Generic class in Java.

A generic class is defined with the following format:

class name<T1, T2, ..., Tn> { /* ... */ }

After the class name there is a type parameter section, delimited by angle brackets (<>). It specifies the type parameters (also called type variables) T1, T2, ..., and Tn.

Generic class creation Java example

In this example we'll create a generic class with two type parameters and use it with different data types.

class GenericClass<K, V> {
  private K key;
  private V value;
  public GenericClass(K key, V value) {
    this.key = key;
    this.value = value;
  }
  public K getKey(){
    return key;
  }
  public V getValue(){
    return value;
  }
}

public class GenericDemo{
  public static void main(String[] args) {
    GenericClass<String, String> g1 = new GenericClass<>("Test", "Value");
    System.out.println("Key- " + g1.getKey());
    System.out.println("Value- " + g1.getValue());

    GenericClass<Integer, Integer> g2 = new GenericClass<>(1, 2);
    System.out.println("Key- " + g2.getKey());
    System.out.println("Value- " + g2.getValue());
    
    GenericClass<Integer, String> g3 = new GenericClass<>(1, "One");
    System.out.println("Key- " + g3.getKey());
    System.out.println("Value- " + g3.getValue());
  }    
}
Output
Key- Test
Value- Value
Key- 1
Value- 2
Key- 1
Value- One

Generic Interface

A Generic interface is created just like Generic class.

interface name<T1, T2, ..., Tn> { /* ... */ }

Some of the rules that are to be followed while implementing a Generic Interface are as given below

  1. If generic type parameter is used with the interface then the class that implements a generic interface has to be a generic class with the same type parameter.
    public class GenericClass<E> implements GenericInterface<E>
    
  2. If you provide a data type with the Interface then you can use a normal class.
    public class NormalClass implements GenericInterface<Integer>
    
  3. A Generic class can have other parameters too apart from the type parameter it has to use because if implementing a generic interface.
    public class GenericClass<K, V, E> implements GenericInterface<E>
    

Generic Method

Any method in Generic class can specify the type parameters of the class and free to add type parameters of its own too. You can have a generic method in a non-generic class too.

Generic Method Java example

Here we’ll have a generic method in a non-generic class.

class TestClass {
  // Generic method
  public <T> void displayArrayElements(T[] arr){
    System.out.println("Elements in Array- " + Arrays.toString(arr));
  }
}

public class GenericDemo{
  public static void main(String[] args) {
    TestClass obj = new TestClass();
    Integer[] intArray = {1, 2, 3, 4, 5, 6, 7};
    Double[] doubleArray = {1.2, 2.3, 3.4, 4.5, 5.6};
    String[] strArray = {"A", "B", "C", "D"};
    obj.displayArrayElements(intArray);
    obj.displayArrayElements(doubleArray);
    obj.displayArrayElements(strArray);
  }    
}
Output
Elements in Array- [1, 2, 3, 4, 5, 6, 7]
Elements in Array- [1.2, 2.3, 3.4, 4.5, 5.6]
Elements in Array- [A, B, C, D]

As you can see if you are writing a generic method with its own type parameters then you need to declare type parameters after the access modifier.

public <T> void displayArrayElements(T[] arr)

You can also specify the actual data type in angular brackets when calling a generic method. Though Java can automatically infer type based on the type of the method arguments so doing that is not mandatory.

obj.displayArrayElements(intArray);
Or this
obj.<Integer>displayArrayElements(intArray);

The Diamond Operator

Java 7 onward it is not mandatory to specify the type arguments required to invoke the constructor of a generic class, you can pass an empty set of type arguments (<>) as long as the compiler can determine, or infer, the type arguments from the context. This pair of angle brackets, <>, is informally called the diamond.

For example, If you have a Generic class defined as given below

public class Test<T> {
  ..
  ..
}

Then you can create its instance like this Java 7 onward.

Test<Integer> obj = new Test<>();

No need to specify Integer on the right hand side, just pass the empty angle brackets <>, type will be inferred automatically.

That's all for the topic Java Generics- Generic Class, Interface And Method. If something is missing or you have something to share about the topic please write a comment.


You may also like

January 24, 2023

Java Generics - Bounded Type Parameters

When you create a generic class or generic method the type parameters can be replaced by any class type but in some scenario you may want to restrict the types that can be used as type arguments in a parameterized type. That can be done using bounded type parameters in Java generics.

Why bounded type parameter needed in Java Generics

Let’s try to understand it with an example when you may need to use bounded parameters. For example, you have a generic class with a method that operates on numbers might only want to accept instances of Number or its subclasses.

First let’s see what happens if you don’t use bounded type parameters. As an example we’ll have a generic class with a method average() that returns the average of an array of numbers. You have defined a generic class so that you can pass array of any type integer, double, float.

public class BoundedType<T> {
  T[] numbers;
  BoundedType(T[] numbers){
    this.numbers = numbers;
  }

  public double average(){
    double sum = 0.0;
    for(int i = 0; i < numbers.length; i++){
      // Compile time error here
      sum += numbers[i].doubleValue();
    }
    double avg = sum/numbers.length;
    return avg;
  }
}

For calculating average suppose you have written a generic class as given above where you have used doubleValue() method to get number of type double for each number in the array. That should work well with any Number type as doubleValue() method is in Number class and it is the super class for all wrapper classes. But you will get compile time error at this line

sum += numbers[i].doubleValue();

Though your intention is to use this generic class always for numbers but there is no way for compiler to know that. For compiler BoundedType<T> means T can later be replaced by any type so there must be some mechanism for compiler to know that type parameter will be restricted to arguments of type Number. That’s where you use Bounded parameters in Java generics.

How to declare bounded type parameters

To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by a super class (upper bound)

<T extends parentclass>

This specifies that T can only be replaced by parentclass, or any child class of parentclass. So, parentclass acts as an upper bound here.

Bounded type parameter example

If we take the same example as above then you can use Number as the upper bound for the type parameter to get rid of the compile time error. With that compiler knows that the type used for the type parameter is going to be a Number or any of its subclass.

public class BoundedType<T extends Number> {
  T[] numbers;
  BoundedType(T[] numbers){
    this.numbers = numbers;
  }
  
  public double average(){
    double sum = 0.0;
    for(int i = 0; i < numbers.length; i++){
      // Compile time error here
      sum += numbers[i].doubleValue();
    }
    double avg = sum/numbers.length;
    return avg;
  }
  
  public static void main(String[] args) {
    Integer[] numArr = {3,4,5};
    BoundedType<Integer> obj = new BoundedType<Integer>(numArr);
    System.out.println("Average is: " + obj.average());
  }
}

Multiple Bounds in Java generics

Type parameter can have multiple bounds too.

<T extends A1 & A2 & A3>

A type variable with multiple bounds is a subtype of all the types listed in the bound. Note that in case of multiple bounds only one of the bounds can be a class others have to be interfaces. If one of the bounds is a class, it must be specified first. For example:

Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }

class D <T extends A & B & C> { /* ... */ }

Bounded type parameters with generic methods in Java

In the above example bounded parameter is used at a class level but you can have generic methods with bounded type parameters too. Consider a scenario where you have a method to count the number of elements in an array greater than a specified element and you have written it as given below.

public static <T> int countElements(T[] numbers, T element) {
  int count = 0;
  for (T e : numbers)
    if (e > element)  // compiler error
      ++count;
  return count;
}

You get compiler error at this line-

if (e > element)

because the greater than operator (>) applies only to primitive types such as short, int, double, long, float, byte, and char. You cannot use the > operator to compare objects.

You will have to use a type parameter bounded by the Comparable<T> interface to compile the code.

public static <T extends Comparable<T>> int countElements(T[] numbers, T element) {
  int count = 0;
  for (T e : numbers)
    if (e.compareTo(element) > 0)  // compiler error
      ++count;
  return count;
}

That's all for the topic Java Generics - Bounded Type Parameters. If something is missing or you have something to share about the topic please write a comment.


You may also like

January 23, 2023

Java Generics - WildCards

While writing generic code you can also use a question mark (?) as type which represents an unknown type and known as wildcard in Java generics.

WildCard in Java generics and class relationship

You can use wildcards to create a relationship between generic classes or interfaces.

In case of non-generic classes

class A { /* ... */ }
class B extends A { /* ... */ }

You can assign reference of child class to parent class.

B b = new B();
A a = b;

But the same assignment does not apply to generic types.

List<B> lb = new ArrayList<>();
List<A> la = lb;   // compile-time error

So, List<B> is not the subtype of List<A> even if A is the parent class.

You can also understand it using the list of integers (List<Integer>) and list of numbers (List<Number>) where Number is the parent class of Integer but List<Integer> is not a subtype of List<Number> in fact, these two types are not related. The common parent of List<Number> and List<Integer> is List<?>. List of unknown type that could be a List<Integer>, List<A>, List<String> and so on.

Using this knowledge of common parent of two generic classes we’ll see how to create a bounded relationship between two generic classes (or interfaces) using three types of wildcards.

Types of wildcards in Java Generics

Based on the limit you want to impose on the relationship between two generic classes there are three types of wildcards.

  • Upper bounded wildcards
  • Lower bounded wildcards
  • Unbounded wildcards

Upper bounded wildcards

To declare an upper-bounded wildcard, use the wildcard character ('?'), followed by the extends keyword, followed by the type that acts as upper bound. Upper bound wildcard matches the upper bound type or any of its subclasses.

For example List<? extends Number> matches a list of type Number or any of its subclasses i.e. List<Integer>, List<Double>, List<Number>.

Upper bounded wildcard Java example

Suppose you want to write a method that can add all the elements of the passed list. Since you have to add the elements so List should have elements of type Integer, Float, Double since Number is the super class for all these wrapper classes so you can create an upper bound using Number class.

import java.util.Arrays;
import java.util.List;

public class WildCard {
  public static void main(String[] args) {
    List<Integer> li = Arrays.asList(1, 2, 3, 4);
    System.out.println("sum = " + addListElements(li));
    //List<Double>
    List<Double> ld = Arrays.asList(1.1, 2.2, 3.3, 4.4);
    System.out.println("sum = " + addListElements(ld));
  }
    
  public static double addListElements(List<? extends Number> list){
    double s = 0.0;
    for (Number n : list) {
      s += n.doubleValue();
    }
    return s;
  }
}
Output
sum = 10.0
sum = 11.0

Lower bounded wildcards

A lower bounded wildcard is expressed using the wildcard character ('?'), following by the super keyword, followed by its lower bound. For example

<? super A>

A lower bounded wildcard restricts the unknown type to be a specific type or a super type of that type. For example you want to write a method that works on lists of Integer and the supertypes of Integer, such as Integer, Number, and Object then you would specify a lower bounded wildcard like this-

List<? super Integer>

Lower bounded wildcards Java example

Suppose you want to write a method that can insert integers to the end of a list and that can be a List of Object, List of Number or List of Integer then you can create a lower bound using Integer class.

import java.util.ArrayList;
import java.util.List;

public class WildCard {
  public static void main(String[] args) {
    // with List<Object>
    List<Object> lo = new ArrayList<Object>();
    insertNumbers(lo);
    
    // with List<Number>
    List<Number> ln = new ArrayList<Number>();
    insertNumbers(ln);
    
    // with List<Integer>
    List<Integer> li = new ArrayList<Integer>();
    insertNumbers(li);
  }
    
  public static void insertNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 10; i++) {
      list.add(i);
    }
    System.out.println("Elements in List- " + list);
  }
}
Output
Elements in List- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Elements in List- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Elements in List- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Unbounded wildcards in Java generics

The unbounded wildcard type is specified using the wildcard character (?).

For example, List<?> represents a list of unknown type.

Unbounded wildcard Java example

Suppose you want to write a method that can print elements of a List of any type then you should use List<?> as method argument. Using List<Object> won’t work as List<Integer>, List<String>, List<Double> are not subtypes of List<Object>.

import java.util.Arrays;
import java.util.List;

public class WildCard {
  public static void main(String[] args) {
    // With List<Integer>
    List<Integer> li = Arrays.asList(5, 6, 7);
    printListElements(li);
    // With List<Double>
    List<Double> ld = Arrays.asList(1.2, 3.8, 8.2);
    printListElements(ld);
  }
    
  public static void printListElements(List<?> list){
    for (Object e : list){
      System.out.print(e + " ");
    }
    System.out.println();
  }
}
Output
5 6 7 
1.2 3.8 8.2 
That's all for the topic Java Generics - WildCards. If something is missing or you have something to share about the topic please write a comment.
You may also like

January 22, 2023

Python I/O - Open, Read, Write Files

In this tutorial you will learn about Python I/O, how to open a file and how to read, write a file in Python.

File types supported by Python

Python has support for following types of files-

  1. Text files- Files where data is stored in the form of characters.
  2. Binary files- Files where data is stored in the form of bytes. Useful for storing images, serializing objects.

Opening a file

File in python is opened using built-in function open() which returns a file object. If the file cannot be opened, an OSError is raised. Syntax of open is as follows.

file_object = open(“filename”, “open_mode”, “buffering”, “encoding”)

Description of arguments-
  1. filename is the path (absolute or relative to the current working directory) of the file to be opened.
  2. open_mode specifies the mode in which the file is opened. It is an optional argument and defaults to 'r' which means open for reading in text mode.
  3. buffering specifies the buffering policy.
    • 0 means buffering is off (only allowed in binary mode)
    • 1 means line buffering (only usable in text mode)
    • integer > 1 indicates buffer size in bytes.
    It is also an optional argument and default buffer size is 4096 or 8192 bytes.
  4. encoding specifies the name of the encoding used to decode or encode the file. This should only be used in text mode.

Modes for opening a file in Python

Mode Description
'r' Open file for reading, starts from the beginning of the file. It is the default mode.
'w' Open file for writing. Any existing data in the file is deleted
'x' Opens for exclusive creation, failing if the file already exists
'a' Open for appending content to an already existing file. If file doesn’t exist a new file is created.
'b' Opens in binary mode
't' Opens in text mode, it is the default mode.
'+' open for updating (reading and writing)

Some examples of using open() function in Python.

# opens file using default mode which is ‘rt’ meaning reading a text file
f  = open(“abc.txt”)  

# opens file for writing in binary mode
f = open(“abc.txt”, 'wb')  

# opens file for writing and reading in binary mode
f = open(“abc.txt”, 'w+b') 

# opens file for reading and writing
f = open(“abc.txt”, 'r+')  

# opens file for writing, existing content is deleted
f = open(“abc.txt”, 'w')  

Closing a file

Once you are done with file I/O operations you should close the file using the close() method. Closing a file immediately frees up any system resources used by it.

f = open("abc.txt", 'a') # file operations .. .. f.close()

After a file object is closed, file object is not accessible any more. If you want to work with the file again, you need to open the file again using the open() function.

Python example to append to a file

Here is an example that opens a file in append mode and append content to the end of the file. Later same file is opened in read mode to read the file.

def write_file(fname):
  try:
    f = open(fname, 'a')
    f.write("This is a test file.\n")
  finally:
    f.close()


def read_file(fname):
  try:
    f = open(fname, 'r')
    str = f.read()
    print('Content- ', str)
  finally:
    f.close()


write_file('F:\\knpcode\\abc.txt')
read_file('F:\\knpcode\\abc.txt')

With Open

In the above example you can see there is a lot of code just to ensure that the file is closed properly. But that try-finally block is required too to ensure that the file is always closed and the resources freed even if an exception is raised.

Another way and the recommended one is to use with keyword in Python when using file objects. By opening a file using with statement file is closed automatically after its suite finishes, even if an exception is raised at some point.

Syntax of using with is as follows-

with open(“file name”, “open mode”) as fileobject:

using with open Python example
def read_file(fname):
    with open(fname, 'r') as f:
        s = f.read()
        print('Content- ', s)
    print('File closed- ', f.closed)


read_file('F:\\knpcode\\abc.txt')

Using f.closed() you can check that the file has been automatically closed.

Python File methods

Here is a complete list of methods in class io.TextIOBase which is the base class for text streams. These are the methods you will use when you get a file object by opening a file in text mode.

Method Description
close() Flush and close this stream.
closed() True if the stream is closed.
fileno() Return a file descriptor (an integer) of the stream
flush() Flush the write buffers of the stream if applicable.
isatty() Return True if the stream is interactive
read(n=-1) Read and return at most n characters from the stream as a single str. If n is negative or None, reads until EOF.
readline(n=-1) Read until newline or EOF and return a single str. If n is specified, at most n characters will be read.
readlines() Read and return a list of lines from the stream.
readable() Return True if the stream can be read from.
seek(offset, fromwhere) Change the stream position to the given byte offset. offset is interpreted relative to the position indicated by fromwhere.
seekable() Return True if the stream supports random access.
tell() Return the current stream position.
truncate(size=None) Resize the stream to the given size in bytes.Resizes to current location if size is not specified.
writable() Return True if the stream supports writing.
write(s) Write the string s to the stream and return the number of characters written.
writelines(lines) Write a list of lines to the stream.

That's all for the topic Python I/O - Open, Read, Write Files. If something is missing or you have something to share about the topic please write a comment.


You may also like

January 21, 2023

How to Synchronize Java HashMap

This post shows how to synchronize HashMap in Java and the HashMap’s thread safe alternative which can be used.

HashMap is not thread safe

If HashMap is accessed by multiple threads concurrently and at least one of the threads modifies the map structurally, you must synchronize HashMap externally. Note that structural modification is any operation that adds or deletes one or more mappings; merely changing the value associated with a key that an instance already contains is not a structural modification.

Options for thread-safe Map

If you want to synchronize HashMap in Java or looking for a thread-safe alternative of HashMap then there are following options.

  1. Use Collections.synchronizedMap() to synchronize Map- This method returns a synchronized (thread-safe) map backed by the specified map. In case you are synchronizing a TreeMap which is a sorted Map you can use synchronizedSortedMap() method
  2. Using ConcurrentHashMap- Another option is to use ConcurrentHashMap from the java.util.concurrent package. All of its operations are thread-safe and it provides better concurrency. ConcurrentHashMap provides better performance by using separate locks for separate buckets rather than synchronizing the whole Map on a single lock.

Using Collections.synchronizedMap()

You can synchronize your HashMap by using Collections.synchronizedMap() method. First we’ll see an example what happens if HashMap is used in a multi-threaded environment without synchronizing it.

In the Java example four threads are created, each of these thread adds 5 elements to the Map. After all the threads are done Map size should be 20.

import java.util.HashMap;
import java.util.Map;

public class MapSynchro implements Runnable{
  private Map<String, String> testMap;
  public MapSynchro(Map<String, String> testMap){
    this.testMap = testMap;
  }

  public static void main(String[] args) {
    // Synchronized Map
    Map<String, String> testMap = new HashMap<String, String>();
    /// 4 threads
    Thread t1 = new Thread(new MapSynchro(testMap));
    Thread t2 = new Thread(new MapSynchro(testMap));
    Thread t3 = new Thread(new MapSynchro(testMap));
    Thread t4 = new Thread(new MapSynchro(testMap));
    
    t1.start();
    t2.start();
    t3.start();
    t4.start();
        
    try {
      t1.join();
      t2.join();
      t3.join();
      t4.join();
    } catch (InterruptedException e) {    
      e.printStackTrace();
    }
    System.out.println("Size of Map is " + testMap.size());
  }
    
  @Override
  public void run() {
    System.out.println("in run method" + Thread.currentThread().getName());
    String str = Thread.currentThread().getName();
    for(int i = 0; i < 5; i++){
      // adding thread name to make element unique
      testMap.put(str+i, str+i);
      try {
        // delay to verify thread interference
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}
Output
in run methodThread-1
in run methodThread-0
in run methodThread-2
in run methodThread-3
Size of Map is 19

In different run I got different HashMap sizes, 17, 18, 19 and 20 because of the thread interference.

Now, if we synchronize the HashMap in the same Java example using Collections.synchronizedMap() method.

public class MapSynchro implements Runnable{
  private Map<String, String> testMap;
  public MapSynchro(Map<String, String> testMap){
    this.testMap = testMap;
  }

  public static void main(String[] args) {
    // Synchronized Map
    Map<String, String> testMap = Collections.synchronizedMap(new HashMap<String, String>());
    /// 4 threads
    Thread t1 = new Thread(new MapSynchro(testMap));
    Thread t2 = new Thread(new MapSynchro(testMap));
    Thread t3 = new Thread(new MapSynchro(testMap));
    Thread t4 = new Thread(new MapSynchro(testMap));
    
    t1.start();
    t2.start();
    t3.start();
    t4.start();
        
    try {
      t1.join();
      t2.join();
      t3.join();
      t4.join();
    } catch (InterruptedException e) {    
      e.printStackTrace();
    }
    System.out.println("Size of Map is " + testMap.size());
  }
  @Override
  public void run() {
    System.out.println("in run method" + Thread.currentThread().getName());
    String str = Thread.currentThread().getName();
    for(int i = 0; i < 5; i++){
      // adding thread name to make element unique
      testMap.put(str+i, str+i);
      try {
        // delay to verify thread interference
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}
Output
in run methodThread-2
in run methodThread-0
in run methodThread-3
in run methodThread-1
Size of Map is 20

Now the size of Map is 20 every time.

Using ConcurrentHashMap

Other than synchronizing HashMap, another option to have a thread safe HashMap is to use ConcurrentHashMap in Java. Let’s see the same example as above using ConcurrentHashMap.

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MapSynchro implements Runnable{
  private Map<String, String> testMap;
  public MapSynchro(Map<String, String> testMap){
    this.testMap = testMap;
  }

  public static void main(String[] args) {
    // Synchronized Map
    Map<String, String> testMap = new ConcurrentHashMap<String, String>();
    /// 4 threads
    Thread t1 = new Thread(new MapSynchro(testMap));
    Thread t2 = new Thread(new MapSynchro(testMap));
    Thread t3 = new Thread(new MapSynchro(testMap));
    Thread t4 = new Thread(new MapSynchro(testMap));
    
    t1.start();
    t2.start();
    t3.start();
    t4.start();
        
    try {
      t1.join();
      t2.join();
      t3.join();
      t4.join();
    } catch (InterruptedException e) {    
      e.printStackTrace();
    }
    System.out.println("Size of Map is " + testMap.size());
  }
  
  @Override
  public void run() {
    System.out.println("in run method" + Thread.currentThread().getName());
    String str = Thread.currentThread().getName();
    for(int i = 0; i < 5; i++){
      // adding thread name to make element unique
      testMap.put(str+i, str+i);
      try {
        // delay to verify thread interference
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}
Output
in run methodThread-2
in run methodThread-0
in run methodThread-3
in run methodThread-1
Size of Map is 20

That's all for the topic How to Synchronize Java HashMap. If something is missing or you have something to share about the topic please write a comment.


You may also like

January 6, 2023

LocalDateTime in Java With Examples

The java.time.LocalDateTime class is part of new date and time API added in Java 8 that represents a date-time in the ISO-8601 calendar system, such as 2019-10-03T11:15:35, it represents a date-time, often viewed as year-month-day-hour-minute-second. LocalDateTime class does not store or represent a time-zone. Instead, it is a description of the date, as used for birthdays, combined with the local time as seen on a wall clock.

LocalDateTime class is immutable thus thread-safe. Since it is marked as final so can't be extended. In this post we’ll see some examples demonstrating usage of Java LocalDateTime class.

Creating instances of LocalDateTime

LocalDateTime class in Java doesn't have any public constructors to obtain an instance, you will use a factory method to get an instance.

1. Using now() method you can obtain the current date-time from the system clock in the default time-zone.

LocalDateTime dateTime = LocalDateTime.now();
System.out.println(dateTime); //2019-10-30T10:29:37.082914100

2. Using of() method you can create an instance of LocalDateTime by passing both year, month, day and hour, minute, second values.

There is also an of() method to create an instance of LocalDateTime by passing both LocalDate and LocalTime instances.

  • of(LocalDate date, LocalTime time)
LocalDateTime dateTime = LocalDateTime.of(2019, 10, 28, 11, 59, 59);
System.out.println(dateTime); //2019-10-28T11:59:59

LocalDateTime for specific time-zone

You can also obtain the current date-time from the system clock in the specified time-zone by passing the zone id.

ZoneId zone1 = ZoneId.of("America/Los_Angeles");
ZoneId zone2 = ZoneId.of("Asia/Kolkata");
LocalDateTime ldt1 = LocalDateTime.now(zone1);
LocalDateTime ldt2 = LocalDateTime.now(zone2);

System.out.println(ldt1); //2019-10-29T22:05:57.729368200
System.out.println(ldt2); //2019-10-30T10:35:57.827541900

Getting date and time values from LocalDateTime

Since LocalDateTime has both date and time values so it has methods to get year, month, day values as well as methods to get hour, minute, second values too.

public class FormatDate {
  public static void main(String[] args) {
    LocalDateTime dateTime = LocalDateTime.of(2019, 10, 28, 11, 59, 59);
    System.out.println("Date-Time: " + dateTime);
    
    System.out.println("Year- " + dateTime.getYear());
    System.out.println("Month- " + dateTime.getMonthValue());
    System.out.println("Day- " + dateTime.getDayOfMonth());
    
    System.out.println("Hour- " + dateTime.getHour());
    System.out.println("Minute- " + dateTime.getMinute());
    System.out.println("Second- " + dateTime.getSecond());
  }
}
Output
Date-Time: 2019-10-28T11:59:59
Year- 2019
Month- 10
Day- 28
Hour- 11
Minute- 59
Second- 59

Formatting LocalDateTime

Using DateTimeFormatter you can specify the pattern for formatting LocalDateTime.

public class FormatDate {
  public static void main(String[] args) {
    // get datetime
    LocalDateTime dateTime = LocalDateTime.now();
    // Format pattern
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
    System.out.println(dateTime.format(formatter));
    // Format pattern
    formatter = DateTimeFormatter.ofPattern("cccc, MMM dd, yyyy hh:mm:ss a");
    System.out.println(dateTime.format(formatter));
  }
}
Output
2019-10-30T11:06:51.899
Wednesday, Oct 30, 2019 11:06:51 AM

Date calculations using LocalDateTime

There are methods to add or subtract days, months and years from a LocalDateTime.

public class FormatDate {
  public static void main(String[] args) {
    // get datetime
    LocalDateTime dateTime = LocalDateTime.now();

    System.out.println("Created Date-Time: " + dateTime);  
    System.out.println("Date after subtraction - " + dateTime.minusDays(40));
    System.out.println("Date after year subtraction - " + dateTime.minusYears(2));
     
    System.out.println("Date after addition - " + dateTime.plusDays(40));
    System.out.println("Date after year addition - " + dateTime.plusYears(2));
  }
}
Output
Created Date-Time: 2019-10-30T11:11:06.820635900
Date after subtraction - 2019-09-20T11:11:06.820635900
Date after year subtraction - 2017-10-30T11:11:06.820635900
Date after addition - 2019-12-09T11:11:06.820635900
Date after year addition – 2021-10-30T11:11:06.820635900

Time calculations using LocalDateTime

There are methods to add or subtract hours, minutes, seconds, nano seconds from a LocalDateTime.

public class FormatDate {
  public static void main(String[] args) {
    // get datetime
    LocalDateTime dateTime = LocalDateTime.now();

    System.out.println("Created Date-Time: " + dateTime);  

    System.out.println("Hour after subtraction- " + dateTime.minusHours(2));
    System.out.println("Minute after subtraction- " + dateTime.minusMinutes(10));
    System.out.println("Second after subtraction- " + dateTime.minusSeconds(20));
    System.out.println("NanoSecond after subtraction- " + dateTime.minusNanos(100));

    System.out.println("Hour after addition- " + dateTime.plusHours(1));
    System.out.println("Minute after addition- " + dateTime.plusMinutes(15));
    System.out.println("Second after addition- " + dateTime.plusSeconds(25));
    System.out.println("NanoSecond after addition- " + dateTime.plusNanos(100));
  }
}
Output
Created Date-Time: 2019-10-30T11:14:07.632356
Hour after subtraction- 2019-10-30T09:14:07.632356
Minute after subtraction- 2019-10-30T11:04:07.632356
Second after subtraction- 2019-10-30T11:13:47.632356
NanoSecond after subtraction- 2019-10-30T11:14:07.632355900
Hour after addition- 2019-10-30T12:14:07.632356
Minute after addition- 2019-10-30T11:29:07.632356
Second after addition- 2019-10-30T11:14:32.632356
NanoSecond after addition- 2019-10-30T11:14:07.632356100

Comparing LocalDateTimes in Java

For comparing two LocalDateTime instances there are the following methods-

  • compareTo(ChronoLocalDateTime<?> other)- Compares this date-time to another date-time. Returns negative value if less than tha passed LocalDateTime instance, positive if greater.
  • isAfter(ChronoLocalDateTime<?> other)- Checks if this date-time is after the specified date-time.
  • isBefore(ChronoLocalDateTime<?> other)- Checks if this date-time is before the specified date-time.
  • isEqual(ChronoLocalDateTime<?> other)- Checks if this date-time is equal to the specified date-time.
public class FormatDate {
  public static void main(String[] args) {
    // get datetime
    LocalDateTime ldt1 = LocalDateTime.of(2019, Month.OCTOBER, 25, 20, 25, 45);
    LocalDateTime ldt2 = LocalDateTime.of(2019, Month.SEPTEMBER, 20, 22, 18, 40);

    System.out.println("Created Date-Time1: " + ldt1);  
    System.out.println("Created Date-Time2: " + ldt2);

    System.out.println(ldt1.compareTo(ldt2));
    System.out.println(ldt2.compareTo(ldt1));

    System.out.println(ldt1.isAfter(ldt2));
    System.out.println(ldt1.isBefore(ldt2));
    System.out.println(ldt1.isEqual(ldt2));
  }
}
Output
Created Date-Time1: 2019-10-25T20:25:45
Created Date-Time2: 2019-09-20T22:18:40
1
-1
true
false
false

Converting String to LocalDateTime

Check this post for String to LocalDateTime conversion- Convert String to Date in Java

Converting LocalDateTime to String

Check this post for LocalDateTime to String conversion- Convert Date to String in Java

That's all for the topic LocalDateTime in Java With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like