Java generic, namely “parameterized type”, is to parameterize the type from the original concrete type, similar to the variable parameter in the method, the type is also defined as a parameter form, and then the specific type (type argument) is passed in when using/calling.
Code that uses generics has many benefits over non-generic code:
- Stronger type checks at compile time.
- Elimination of casts.
- Enabling programmers to implement generic algorithms.
Type Parameter Naming Conventions
The most commonly used type parameter names are:
- E – Element (used extensively by the Java Collections Framework)
- K – Key
- N – Number
- T – Type
- V – Value
- S,U,V etc. – 2nd, 3rd, 4th types
Generics make types as parameters when defining classes, interfaces and methods.
Generic class
Syntax
public class ClassName<T> {}
Example
package com.example.generic;
public class GenericClass<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public static void main(String[] args) {
GenericClass<String> stringGenericClass = new GenericClass<>();
stringGenericClass.setT("Hello");
System.out.println(stringGenericClass.getT());
}
}
Generic interface
Syntax
public interface InterfaceName<T> {}
Example 1
package com.example.generic;
public class GenericInterfaceImpl implements GenericInterface<String> {
@Override
public String service(String s) {
return s.toUpperCase();
}
public static void main(String[] args) {
GenericInterfaceImpl genericInterface = new GenericInterfaceImpl();
String abc = genericInterface.service("abc");
System.out.println(abc);
}
}
Example 2
package com.example.generic;
public class GenericInterfaceImpl2<T> implements GenericInterface<T> {
@Override
public T service(T t) {
return t;
}
public static void main(String[] args) {
GenericInterfaceImpl2<String> stringGenericInterfaceImpl2 = new GenericInterfaceImpl2<>();
String str = stringGenericInterfaceImpl2.service("Hello");
System.out.println(str);
System.out.println("----------------------------");
GenericInterfaceImpl2<Integer> integerGenericInterfaceImpl2 = new GenericInterfaceImpl2<>();
Integer integer = integerGenericInterfaceImpl2.service(1000);
System.out.println(integer);
}
}
Generic method
Syntax
public <T, R> T methodName(T t, R r) {}
Example
package com.example.generic;
public class GenericMethod {
public <T> void show(T t) {
System.out.println(t);
}
public <T> T services(T t) {
return t;
}
public static void main(String[] args) {
GenericMethod genericMethod = new GenericMethod();
genericMethod.show(1);
String string = genericMethod.services("Hello");
Double aDouble = genericMethod.services(122.1);
}
}
Upper Bounded Wildcards
Generic Wildcards
In generic code, the question mark (?), called the wildcard, represents an unknown type. 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.
Generic wildcards including:
- upper bounded wildcards
List<? extends Foo> list
- lower bounded wildcards
List<? super Foo> list
- wildcard capture
List<?> list
Example
package com.example.generic;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class testGenericBoundedWildcards {
public static void main(String[] args) {
List<GrandeFather> grandeFatherList = new ArrayList<>();
List<Father> fatherList = new ArrayList<>();
List<Son> sonList = new ArrayList<>();
/**
* Upper bounded wildcard <? Expanded Father>, can only match any subtype of Father and Father.
* UpperBound(grandeFatherList); // error
*/
UpperBound(fatherList);
UpperBound(sonList);
/**
* In a similar way, a lower bounded wildcard restricts the unknown type to be a specific type or a super type of that type.
* LowerBound(sonList); // error
*/
LowerBound(fatherList);
LowerBound(grandeFatherList);
/**
* wildcard capture
*/
List<String> stringList = Arrays.asList("a", "b", "c");
List<Integer> integerList = Arrays.asList(1, 2, 3, 4);
foo(stringList);
foo(integerList);
}
public static void UpperBound(List<? extends Father> list) {
list.forEach(System.out::println);
}
public static void LowerBound(List<? super Father> list) {
list.forEach(System.out::println);
}
public static void foo(List<?> list) {
System.out.println(list.size());
}
}
Reference
https://docs.oracle.com/javase/tutorial/java/generics/why.html