Java Generics interview questions

Here is a list interview questions that may be asked in interviews. Java Generics as a feature is one of the most controversial features added to Java Language. As a programmer, it is very important that the basic concepts of Generics should be very clear.  I have prepared a list of basic interview questions that are frequently asked in interviews.

Basic Java Generics Questions

What is Generic programming?

Generic Programming is a programming pattern  in which algorithms are written is such a way that types can be provided as parameters.

What are Generics?

Generics is a way of implementing generic programming. Generics provides a way to declare one common functionality for many Types rather than specifying individually for each Type. Generics are in Java since 1.5.

Java Generics concepts

Java Generics concepts

Are Java Generics Run-Time or Compile-Time feature? Or What is Type Erasure?

This is my favorite interview question because if a person understands this then using generics should be quite clear to him.

Generics allows interfaces and classes to be parameterised while defining Class , Interface or a method.  Let us have a look at the below code.  We want to define a list of Integer‘s.

List<Integer>  ints = new ArrayList<Integer>();

In the above code a list of integers is defined. We have parameterised List interface with Integer type. This makes usage of List safe. As it won’t allow any other type to be added to the list. e.g. adding a Long will have a compile time error. This makes java code more safe. But when the code is compiled, the type is erased and is not visible in compiled bytecode.

List  list = new ArrayList();

So the above code when compiled would have the almost same bytecode as the one with type defined.  This is called Type Erasure.  So Generics is available only at compile type.

NOTE: Sometime while generating the bytecode a cast may be needed. That is why the bytecode generated with Generic parameters and code without Generic parameters may be slightly different. A Bridge method may be needed while generating the bytecode.

Why should Generics be used? Or What are the advantages or Generics?

Generics enables strong type checking:

Let’s see the below example

List<Integer> list = new ArrayList<Integer>();
list.add(Integer.valueOf(1));
list.add(Long.valueOf(1)); // will have compile error

We tried to add an object of Type Long into a list that was parameterised with  Integer. But the code doesn’t compile because of the type check on compile.  But we can add an Integer and Long into a List by removing the Type parameter from the List.

 List list = new ArrayList();
 list.add(Integer.valueOf(1));
 list.add(Long.valueOf(1));

And the above code compiles and runs without any errors. But the problem arises when you try to get the values back from the List. How would one know the type of an object at a particular index in List.

Elimination of casts:

Generics makes code more safe by making the type checks at compile time and eliminating casts. So we created a list above that has Integer and Long value. So how do we get them from the List. We would have to cast the objects from the List and know exactly at which index is which Type. which makes it very difficult to debug and maintain this code.

 Integer intObj = (Integer) list.get(0);
 Long longObj = (Long) list.get(1);

The above code compiles but with a cast. If I get the casting wrong like the below code, then there is no way to know of it at compile time. But at runtime it would cause a ClassCastException.

 Integer intObj = (Integer) list.get(1);
 Long longObj = (Long) list.get(0);
 

Develop Generic Solutions:

Generics also provide a benefit that generic algorithms can be developed. Here is a very simple method that can convert from array to collection. And this method can then be applied on any type.

static <T> void convertArrayToCollection(T[] array, Collection<T> collection) {
	    for (T o : array) {
	    	collection.add(o); 
	    }
}

So we can convert any type of array to collection of that type with one generic method.  Java Collections api is one the Java implementations that uses Generics to provide generic solution. By using generics, programmers can implement generic algorithms that work on collections of different types, can be customized, and are type safe and easier to read.

Are parameterized types covariant?

Number is a supertype of Integer. So the following expression is allowed in Java. And this is called convenience.

Number num = new Integer(1);

Parameterized types are not covariant. So the below statement is not allowed.

List<Number> intList = new ArrayList<Integer>(); // compile error

But remember, the following statement is a valid statement. We could assign an ArrayList to supertype Collection with the same parameterized type.

Collection<Number> numberCollections = new ArrayList<Number>();

Can you create generic arrays?

No. as it will not be type safe. Arrays of supertype objects is an array of a subtype objects. So arrays are covariant. And the following statement is valid.

Number[] numArray = new Integer[10];

We put a Long object into an array that was created as Integer array. And this piece of code is also compiled

Number[] numArray = new Integer[10];
numArray[0] = Long.valueOf(2);

But when we run this code we get the following exception

Exception in thread "main" java.lang.ArrayStoreException: java.lang.Long
	at com.programtalk.learn.generics.Generics.test(Generics.java:26)
	at com.programtalk.learn.generics.Generics.main(Generics.java:16)


Arrays have runtime information about the type of data that can be stored in Array. As soon as we try to put a Long object into an Array that was initialized with String array, the ArrayStoreException was thrown.  Do you see the problem with the Generics now. Generic Type information is available only at compile type. And Arrays need the information at runtime so as to make them safe. Arrays can no longer get the type information needed at runtime and hence would allow unwanted types in the Array. We can see the example below:

 Comparable<Number>[] comaparableArray = new Comparable<Integer>[10];
 comaparableArray[0] = Long.valueOf(0l);
 comaparableArray[1] = Integer.valueOf(0);

In the above example, we are assuming that if Java allowed to create a generic Array. Since arrays are covariant, so  we could assign a Comparable<Integer> array to Comparable<Number> array.  Next we would add a Long object to array and then an  Integer object.  So these assignments should have no compile time issues.  Now when you run this code, array has to store the runtime information about Comparable<Integer> array.  But due to Generics Type erasure at runtime, Array can only have the runtime information about Comparable. So Array won’t be able to find at runtime that it has not to allow the Long object. So the array is no longer type safe.

What is a wildcard in generics? And how can it be used?

Question mark (?) is known as a wildcard. It is used for unknown type.

Upper Bound Wildcard : An upper bound wild card relaxes the restrictions on variable. An upper bound wildcard is defined as ? extends Type. Let’s look at the example below

	private void printNumbers(List<? extends Number> numbers){
		for(Number e : numbers){
			System.out.println(e.intValue());
		}
	}

In order at print the int value of a Integer, Long or Double, I created a method that accepts all the Lists that have items of Type extending class Number.

Unbounded Wildcard:  Unbound wildcard is specified by just defining ?.  e.g. defining a List of objects List<?> Here we are defining a List of objects. So what is this useful for. You can use this methods from Object class or the methods of List like size(). Here is good example of unbounded Wildcard usage.

	private void printListInfo(List<?> anyObjectList){
		System.out.println("size of list : " + anyObjectList.size());
		for(Object e : anyObjectList){
			System.out.println(e);
		}
	}

Lower Bounded Wildcard : A lower bound wildcard restricts a type to be a specific Type or a parent of that Type. It is written as ? super Type.

	public static void addLongNumbers(List<? super Long> list) {
	    for (long i = 1; i <= 10; i++) {
	        list.add(i);
	    }
	}

What are the guidelines in using unbounded, upper bounded and lower bounded wildcards? 

Looking at the definitions of the various wild card usages, we can usually try to follow following guidelines:

  1. Returning a wildcard from a method should be avoided because it makes the method users have to handle the wildcards.
  2. When a parameter is passed that is used by a structure(e.g a method), it is defined with an upper bounded wildcard, using the extends keyword.
  3. In case the parameter needs only functionality from Object class then using a unbounded wildcard can be used.
  4. When a parameter is passed that is used by the code outside the structure(e.g a method), then it is defined with a lower bounded wildcard, using the super keyword.
  5. When a variable needs to be used both outside and inside a structure(e.g a method) then wildcards should not be used.

super vs extend in generics?

This questions has a great explanation here in stackoverflow.

How can you use multiple bounds in generics?

In Java, a class can implement more than one interface and can also extend one class. So as to have a type param that specifies all the class that are of more than one Type. One example is Integer class that extends Number and also implements comparable. Multiple bounds can be specified by using &.

In the below example we will create a method that accepts only an object that is of type Number and Comparable.

	public static <T extends Number & Comparable<? super T>> int compareNumbers(T t1, T t2) {
		return t1.compareTo(t2);
	}

Which Types in java cannot have type parameters?

Enum types, anonymous inner classes and exception classes  cannot be generic

What is the difference between List<Object> and List<?> 

With List<Object> you can add any subtype of Object into the List but List<?> allows only adding a null value.

Food for thought

I am adding some generic tricks and questions here. You can provide your explanations in the comments as to why each trick works or what is the answer to a particular question.

1. Make a List almost readonly with the exception of null.

List<Long> longList = new ArrayList<>();
 List<? extends Number> numberList = longList;
 numberList.add(new Long(35)); // compile-time error "The method add(capture#1-of ? extends Number) in the type List<capture#1-of ? extends Number> is not applicable for the arguments (Long)"
numberList.add(null); // only this will work

2. Creation of a Generic Array

private static <T> T[] createGenericArray(Class<T> tClass, int size){
		@SuppressWarnings("unchecked")
		T[] gArr = (T[])java.lang.reflect.Array.newInstance(tClass, size); 
		return gArr;
	}

3. What is PECS (Producer Extends Consumer Super)?

4. What is diamond Operator?

5. Can static method use generic param from declaring class?

6. What is Bridge method?

Some invaluable resources for Generics

You may also like

Like this post? Don’t forget to share it!

Leave a Reply

Your email address will not be published. Required fields are marked *