Saturday, January 28, 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

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

Thursday, January 26, 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

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

Tuesday, January 24, 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

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

Sunday, January 22, 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