Java list of generic types

Generics in java were introduced as one of features in JDK 5. Personally, I find the angular brackets used in generics very fascinating and it always forces me to have another thought where I use it OR see it written in somebody elses code. To be very frank, I have been using generics since a long time now but still I feel not fully confident to use it blindly. In this tutorial, I will be covering everything I find useful with java generics, and things related to them. If you think that I can use more precise words at any part of the tutorial, or an example can be added or simply you do not agree with me; drop me a comment. I will be glad to know your point of view.

Table of content 1] Why Generics? 2] How Generics works in Java 3] Types of Generics? i] Generic Type Class or Interface ii] Generic Type Method or Constructor 4] Generic Type Arrays 5] Generics with Wildcards i] Unbounded Wildcards ii] Bounded Wildcards a] Upper Bounded Wildcards b] Lower Bounded Wildcards 6] What is not allowed to do with Generics?

Java Generics is a technical term denoting a set of language features related to the definition and use of generic types and methods . In java, Generic types or methods differ from regular types and methods in that they have type parameters.

Java Generics are a language feature that allows for definition and use of generic types and methods.

Generic types are instantiated to form parameterized types by providing actual type arguments that replace the formal type parameters. A class like LinkedList is a generic type, that has a type parameter E . Instantiations, such as LinkedList or a LinkedList, are called parameterized types, and String and Integer are the respective actual type arguments.

1] Why Generics?

If you closely look at java collection framework classes then you will observe that most classes take parameter/argument of type Object and return values from methods as Object. Now, in this form, they can take any java type as argument and return the same. They are essentially heterogeneous i.e. not of a particular similar type.

Programmers like us often wanted to specify that a collection contains elements only of a certain type e.g. Integer or String or Employee. In the original collection framework, having homogeneous collections was not possible without adding extra checks before adding some checks in code. Generics were introduced to remove this limitation to be very specific. They add this type checking of parameters in your code at compile-time, automatically. This saves us writing a lot of unnecessary code which actually does not add any value in run-time if written correctly.

In layman,s term, generics force type safety in java language.

Without this type of safety, your code could have infected by various bugs that get revealed only in runtime. Using generics, makes them highlighted in compile time itself and make you code robust even before you get the bytecode of your java source code files.

Generics add stability to your code by making more of your bugs detectable at compile time.

So now we have a fair idea of why generics are present in java in the first place. The next step is to get some knowledge about how they work in java. What actually happens when you use generics in your source code.

2] How Generics works in Java

In the heart of generics is type safety. What exactly is type safety? Its just a guarantee by compiler that if correct Types are used in correct places then there should not be any ClassCastException in runtime. A usecase can be list of Integer i.e. List. If you declare a list in java like List, then java guarantees that it will detect and report you any attempt to insert any non-integer type into above list.

Another important term in java generics is type erasure. It essentially means that all the extra information added using generics into source code will be removed from bytecode generated from it. Inside bytecode, it will be old java syntax which you will get if you dont use generics at all. This necessarily helps in generating and executing code written prior to java 5 when generics were not added in language.

Lets understand with an example.

List list = new ArrayList[]; list.add[1000]; //works fine list.add["lokesh"]; //compile time error;

When you write above code and compile it, you will get below error: The method add[Integer] in the type List is not applicable for the arguments [String]. The compiler warned you. This exactly is generics sole purpose i.e. Type Safety.

The second part is getting the byte code after removing the second line from the above example. If you compare the bytecode of the above example with/without generics, then there will not be any different. Clearly compiler removed all generics information. So, the above code is very much similar to the below code without generics.

List list = new ArrayList[]; list.add[1000];

Precisely, Generics in Java is nothing but a syntactic sugar to your code for Type Safety and all such type information is erased by Type Erasure feature by the compiler.

3] Types of Generics?

Now we have some understanding of what generics are all about. Now start exploring other important concepts revolving around generics. I will start by identifying the various ways, generics can be applied into sourcecode.

Generic Type Class or Interface

A class is generic if it declares one or more type variables. These type variables are known as the type parameters of the class. Lets understand with an example.

DemoClass is a simple java class, which has one property t [can be more than one also]; and type of property is Object.

class DemoClass { private Object t; public void set[Object t] { this.t = t; } public Object get[] { return t; } }

Here we want that once initialized the class with a certain type, class should be used with that particular type only. e.g. If we want one instance of class to hold value t of type String, then programmer should set and get the only String type. Since we have declared property type to Object, there is no way to enforce this restriction. A programmer can set any object, and can expect any return value type from get method since all java types are subtypes of Object class.

To enforce this type restriction, we can use generics as below:

class DemoClass { //T stands for "Type" private T t; public void set[T t] { this.t = t; } public T get[] { return t; } }

Now we can be assured that class will not be misused with wrong types. A sample usage of DemoClass will look like this:

DemoClass instance = new DemoClass[]; instance.set["lokesh"]; //Correct usage instance.set[1]; //This will raise compile time error

The above analogy is true for the interfaces as well. Lets quickly look at an example to understand, how generics type information can be used in interfaces in java.

//Generic interface definition interface DemoInterface { T2 doSomeOperation[T1 t]; T1 doReverseOperation[T2 t]; } //A class implementing generic interface class DemoClass implements DemoInterface { public Integer doSomeOperation[String t] { //some code } public String doReverseOperation[Integer t] { //some code } }

I hope, I was enough clear to put some light on generic classes and interfaces. Now its time to look at generic methods and constructors.

Generic Type Method or Constructor

Generic methods are much similar to generic classes. They are different only in one aspect that the scope of type information is only inside the method [or constructor]. Generic methods are methods that introduce their own type parameters.

Lets understand this with an example. Below is a code sample of a generic method that can be used to find all occurrences of a type parameter in a list of variables of that type only.

public static int countAllOccurrences[T[] list, T item] { int count = 0; if [item == null] { for [ T listItem : list ] if [listItem == null] count++; } else { for [ T listItem : list ] if [item.equals[listItem]] count++; } return count; }

If you pass a list of String and another string to search in this method, it will work fine. But if you will try to find an Number into list of String, it will give compile-time error.

The same as above can be an example of a generic constructor. Lets take a separate example for a generic constructor as well.

class Dimension { private T length; private T width; private T height; //Generic constructor public Dimension[T length, T width, T height] { super[]; this.length = length; this.width = width; this.height = height; } }

In this example, Dimension classs constructor has the type information also. So you can have an instance of dimension with all attributes of a single type only.

4] Generic Type Arrays

Array in any language have same meaning i.e. an array is a collection of similar type of elements. In java, pushing any incompatible type in an array on runtime will throw ArrayStoreException. It means array preserve their type information in runtime, and generics use type erasure or remove any type of information in runtime. Due to the above conflict, instantiating a generic array in java is not permitted.

public class GenericArray { // this one is fine public T[] notYetInstantiatedArray; // causes compiler error; Cannot create a generic array of T public T[] array = new T[5]; }

In the same line as above generic type classes and methods, we can have generic arrays in java. As we know that an array is a collection of similar type of elements and pushing any incompatible type will throw ArrayStoreException in runtime; which is not the case with Collection classes.

Object[] array = new String[10]; array[0] = "lokesh"; array[1] = 10; //This will throw ArrayStoreException

The above mistake is not very hard to make. It can happen anytime. So its better to provide the type information to array also so that error is caught at compile time itself.

Another reason why arrays do not support generics is that arrays are covariant, which means that an array of supertype references is a supertype of an array of subtype references. That is, Object[] is a supertype of String[] and a string array can be accessed through a reference variable of type Object[].

Object[] objArr = new String[10]; // fine objArr[0] = new String[];

5] Generics with Wildcards

In generic code, the question mark [?], called the wildcard, represents an unknown type. A wildcard parameterized type is an instantiation of a generic type where at least one type argument is a wildcard. Examples of wildcard parameterized types are Collection. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type [though it is better programming practice to be more specific]. The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype.

Having wild cards at difference places have different meanings as well. e.g.

  • Collection denotes all instantiations of the Collection interface regardless of the type argument.
  • List denotes all list types where the element type is a subtype of Number.
  • Comparator coll = new ArrayList[]; //OR List pair = new Pair[];

    And below are not valid uses of wildcards, and they will give compile-time error.

    List

Chủ Đề