Java Genrics is one of the most important features introduced in Java 5.
If you've been using Java Collections and version 5 or later, I'm sure you've used it.
Generics with collection classes in Java are very easy, but they provide more functionality than just creating collection types.
We'll try to learn the function of generics in this article. If we use technical terms, the understanding of generics can sometimes become confusing, so I will try to keep it as simple and understandable as possible.
1. Generics in Java
Generics have been added to Java 5 to provide compile time type checking and to eliminate the common risks of ClassCastException using collection classes. The entire collection framework is rewritten to use generics for type security. Let's see how generics help us use collection classes safely.
List list = new ArrayList(); list.add("abc"); list.add(new Integer(5)); for(Object obj : list){ String str=(String) obj; }
The above code compiles well, but raises ClassCastException at run time, because we tried to cast objects in the list to String, and one of the elements was of type Integer. After Java 5, we use the following collection classes.
List<String> list1 = new ArrayList<String>(); // java 7 ? List<String> list1 = new ArrayList<>(); list1.add("abc"); //list1.add(new Integer(5)); / / compilation error for(String str : list1){ //no type casting needed, avoids ClassCastException }
Note that when creating the list, we have specified that the type of elements in the list is String. Therefore, if we try to add any other type of object to the list, the program raises a compile time error. Also note that in the loop, we do not need to type the elements in the list, so ClassCastException is removed at run time.
2. Java general class
We can use generic types to define our own classes. Generic types are classes or interfaces that are parameterized by types. We use angle brackets (< >) to specify type parameters.
To understand the benefits, let's assume a simple class:
package com.journaldev.generics; public class GenericsTypeOld { private Object t; public Object get() { return t; } public void set(Object t) { this.t = t; } public static void main(String args[]){ GenericsTypeOld type = new GenericsTypeOld(); type.set("Pankaj"); String str = (String) type.get(); //type casting, error prone and can cause ClassCastException } }
Note that when using this class, we must use type conversion, and it can generate ClassCastException at run time. Now, we will replace the same class shown below with a Java generic class.
package com.journaldev.generics; public class GenericsType<T> { private T t; public T get(){ return this.t; } public void set(T t1){ this.t=t1; } public static void main(String args[]){ GenericsType<String> type = new GenericsType<>(); type.set("Pankaj"); //valid GenericsType type1 = new GenericsType(); //raw type type1.set("Pankaj"); //valid type1.set(10); //valid and autoboxing support } }
Note the use of the GenericsType class in the main method. We don't need to do type conversion, and we can remove ClassCastException at run time. If we don't provide a type when we create it, the compiler warns, "GenericsType is the original type.
References to generic types GenericsType should be parameterized. " When we do not provide a type, it becomes a type Object, so it allows String and Integer objects. However, we should always try to avoid this situation, because when dealing with primitive types that may produce runtime errors, we must use type conversions.
Also note that it supports Java auto boxing.
3. Java universal interface
The compatible interface is a good example of generics in the interface. It is written as follows:
package java.lang; import java.util.*; public interface Comparable<T> { public int compareTo(T o); }
In a similar way, we can create generic interfaces in Java. We can also have multiple type parameters like the Map interface. Similarly, we can provide parameterized values for parameterized types, such as new HashMap < string, list < string > > (); valid.
4. Java common types
Java common type naming convention can help us understand code easily, and having naming convention is one of the best practices of Java programming language. Therefore, generics also have their own naming conventions. In general, the type parameter name is a single uppercase letter to distinguish it from Java variables. The most common type parameter names are:
- E – elements are widely used by the Java Collections Framework, such as ArrayList, Set, etc.
- K – key (used in Map)
- N - figures
- T - type
- V – value (used in Map)
- S, U, V, etc. - second, third, fourth type
5. Java general methods
Sometimes we don't want the entire class to be parameterized, in which case we can create java generic methods. Because constructors are a special method, we can also use generic types in constructors.
This is a class that shows examples of Java generic methods.
package com.journaldev.generics; public class GenericsMethods { //Java Generic Method public static <T> boolean isEqual(GenericsType<T> g1, GenericsType<T> g2){ return g1.get().equals(g2.get()); } public static void main(String args[]){ GenericsType<String> g1 = new GenericsType<>(); g1.set("Pankaj"); GenericsType<String> g2 = new GenericsType<>(); g2.set("Pankaj"); boolean isEqual = GenericsMethods.<String>isEqual(g1, g2); //above statement can be written simply as isEqual = GenericsMethods.isEqual(g1, g2); //This feature, known as type inference, allows you to invoke a generic method as an ordinary method, without specifying a type between angle brackets. //Compiler will infer the type that is needed } }
Note that the isEqual method signature of the shows the syntax for using generic types in methods. Also, notice how to use these methods in our Java programs. We can specify the types when we call these methods, or we can call them just like ordinary methods. Java compilers are smart enough to determine the type of variables to use, a feature called type variables.
6. Java generic binding type parameters
Suppose we want to restrict the types of objects that can be used in parameterized types, for example, in methods that compare two objects, and we want to make sure that the accepted objects are comparable. To declare a bounded type parameter, list the name of the type parameter, then the extended keyword, plus its upper limit, as follows.
public static <T extends Comparable<T>> int compare(T t1, T t2){ return t1.compareTo(t2); }
Calls to these methods are similar to unbounded methods except that if we try to use any non Comparable class, a compile time error is raised.
Binding type parameters can be used with methods as well as classes and interfaces.
Java generics also support multiple scopes, < T extends A & B & C >. In this case, A can be an interface or A class. If A is A class, then B and C should be interfaces. We cannot have more than one class in more than one scope.
7. Java generics and inheritance
We know that if A is A subclass of B, Java inheritance allows us to assign variable A to another variable B. Therefore, we may think that any generic type of A can be assigned to A generic type of B, but this is not the case. Let's take A look at it in A simple program.
package com.journaldev.generics; public class GenericsInheritance { public static void main(String[] args) { String str = "abc"; Object obj = new Object(); obj=str; // works because String is-a Object, inheritance in java MyClass<String> myClass1 = new MyClass<String>(); MyClass<Object> myClass2 = new MyClass<Object>(); //myClass2=myClass1; // compilation error since MyClass<String> is not a MyClass<Object> obj = myClass1; // MyClass<T> parent is Object } public static class MyClass<T>{} }
8. Java common classes and subtypes
We can extend or implement a generic class or interface. The relationship between the type parameters of one class or interface and the type parameters of another class or interface is determined by the extend and implementation clauses.
For example, ArrayList implements a List that extends Collection, so ArrayList is a subtype of List, and List is a subtype of Collection.
As long as the type parameter is not changed, the subtype relationship is preserved, and the following shows an example of multiple type parameters.
interface MyList<E,T> extends List<E>{ }
The subtypes of List can be mylist < string, Object >, mylist < string, integer >, etc.
9. Java universal wildcard
Question mark (?) Is a wildcard in a generic, indicating an unknown type. Wildcards can be used as types of parameters, fields, or local variables, and sometimes as return types. Wildcards cannot be used when calling generic methods or instantiating generic classes. In the following sections, we will learn about upper bound wildcards, lower bound wildcards, and wildcard capture.
9.1) Java Generic upper bound wildcard
Upper limit wildcards are used to relax restrictions on variable types in methods. Suppose we are going to write a method that will return the sum of the numbers in the list, then our implementation will be like this.
public static double sum(List<Number> list){ double sum = 0; for(Number n : list){ sum += n.doubleValue(); } return sum; }
Now, the problem with this implementation is that it doesn't work with Integers or Doubles, because we know that lists are not related to lists, which is useful when using high-level wildcards. We use common wildcards with the extends keyword and the parent class or interface, which allows us to pass parameters of the parent subclass type.
You can modify the above implementation just like the following program.
package com.journaldev.generics; import java.util.ArrayList; import java.util.List; public class GenericsWildcards { public static void main(String[] args) { List<Integer> ints = new ArrayList<>(); ints.add(3); ints.add(5); ints.add(10); double sum = sum(ints); System.out.println("Sum of ints="+sum); } public static double sum(List<? extends Number> list){ double sum = 0; for(Number n : list){ sum += n.doubleValue(); } return sum; } }
Just like writing code according to the interface, in the above methods, we can use all methods of the upper class number. Note that for the upper bound list, we do not allow any objects to be added to the list except for null. If we try to add elements to the list within the sum method, the program will not compile.
9.2) Java generic infinite wildcard
Sometimes, we want the generic method to work for all types, in which case you can use unbounded wildcards. And use <? Extends Object >.
public static void printData(List<?> list){ for(Object obj : list){ System.out.print(obj + "::"); } }
We can provide List or List or any other type of Object List parameter for the PrintData method. Like the upper limit List, we can add anything to the List.
9.3) Java generic lower bound wildcard
Suppose we want to add the whole to the integer List in the method, we can keep the parameter type as List, but we can bundle it with Integers, and List and List can also hold Integers, so we can use the lower limit wildcard to implement. We use the super keyword and the generic wildcard of the lower bound class. To achieve this.
We can pass any supertype of the lower bound or lower bound as a parameter, in which case the java compiler allows the lower bound object type to be added to the list.
public static void addIntegers(List<? super Integer> list){ list.add(new Integer(50)); }
10. Subtype with generic wildcards
List<? extends Integer> intList = new ArrayList<>(); List<? extends Number> numList = intList; // OK. List<? extends Integer> is a subtype of List<? extends Number>
11. Java generic type refactoring
Java generics have been added to provide type checking at compile time and are not used at run time, so the java compiler uses the type change feature to remove all generic type checking code from bytecode and insert type conversions if necessary. Type definitions ensure that new classes are not created for parameterized types; therefore, generics do not generate runtime waste.
For example, if we have the following generic classes;
public class Test<T extends Comparable<T>> { private T data; private Test<T> next; public Test(T d, Test<T> n) { this.data = d; this.next = n; } public T getData() { return this.data; } }
The Java compiler replaces the bounded type parameter T with the first binding interface compatible, as follows:
public class Test { private Comparable data; private Test next; public Node(Comparable d, Test n) { this.data = d; this.next = n; } public Comparable getData() { return data; } }
12. Generic FAQ
12.1) why do we use generics in Java?
Generics provide powerful compile time type checking and reduce the risk of ClassCastException and explicit object conversion.
12.2) what is T in generics?
We use to create generic classes, interfaces, and methods. We replace T with the actual type when we use it.
12.3) how do generics work in Java?
Generic code ensures type safety. The compiler uses types to remove all type parameters at compile time in advance to reduce runtime overloading.
13. Java generics - read more
- Generics do not support subtypes, so list < number > numbers = new ArrayList < integer > (); will not be compiled
- We were unable to create a generic replica, so list < integer > [] array = new ArrayList < integer > [10] could not be compiled
This is all Java generics. Java generics are very large. It takes a lot of time to understand and use it effectively. This article provides the basic details of generics and how to use generics to extend the type security of programs.
"Every step is a thousand miles", I hope you can: have a dream for a horse to live everywhere! Come on, boy!
Pay attention to the public account: "Java confidant", update java knowledge every day, and look forward to your coming!
- Send "Group" and make progress with 100000 programmers.
- Send "interview" to get interview materials and video strategies of BATJ.
- Send "play algorithm" to get "play algorithm" series of video tutorials.
- Do not send "1024"...
Daily benefits