1. 为什么要使用泛型
2. 泛型在Java中如何生效
3. 泛型类型
1) 泛型类或接口
一个类使用了一个或者多个类型的变量,则这个类是个泛型类。这些类型的变量称为类型参数。用一个例子来理解:
class DemoClass<T> {
//T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
使用泛型类之后,一方面支持这个类有多种类型参数,另一方面避免发生编译错误。比如:
DemoClass<String> instance = new DemoClass<String>();
instance.set("lokesh"); //Correct usage
instance.set(1); //This will raise compile time error
同理接口中使用类似,例如:
//Generic interface definition
interface DemoInterface<T1, T2>
{
T2 doSomeOperation(T1 t);
T1 doReverseOperation(T2 t);
}
//A class implementing generic interface
class DemoClass implements DemoInterface<String, Integer>
{
public Integer doSomeOperation(String t)
{
//some code
}
public String doReverseOperation(Integer t)
{
//some code
}
}
2) 泛型方法/构造函数
泛型方法/构造函数与泛型类很类似,但泛型方法的类型信息的范围仅限于方法内部,并且其引入了自身的类型参数。
4. 泛型数组
在java中,在运行时将任何不兼容的类型推送到数组中都引发异常,这意味着数组在运行时保留其类型信息,而泛型在运行时会擦除或删除任何类型的信息。所以在java中不允许实例化泛型数组。
// causes compiler error; Cannot create a generic array of T
public T[] array = new T[5];
数组不支持泛型的另一个原因是数组是协变的,这意味着超类型引用的数组是子类型引用数组的父类型。也就是说,Object[]是String[]的超类型,可以通过Object[]类型的引用变量访问字符串数组。
Object[] objArr = new String[10]; // fine
objArr[0] = new String();
5. 带通配符的泛型
为什么要使用通配符?
通配符用于提示使用者,java泛型所实施的规则,即在使用通配符的任何特定场景中,哪些类型是有效的。
泛型中 ? 代表通配符,表示一个未知的类型。通配符的参数化类型是泛型类型的一个实例,表示该泛型类型中至少有一个参数是通配符。通配符可以用于多种情况,比如作为参数、字段、局部变量的类型,或者作为返回类型。但通配符不能作为泛型方法调用、泛型类实例创建、超类的类型参数。
泛型用于不同位置的含义也不同:
Collection 表示该集合接口的所有实例化都与类型参数无关;
List<? extends Number> 表示元素类型是Number子类型的所有列表类型;
Comparator<? super T> 表示类型参数是T的超类的所有Comparator接口的实例化
以下是正确的通配符使用:
Collection<?> coll = new ArrayList<String>();
List<? extends Number> list = new ArrayList<Long>();
Pair<String,?> pair = new Pair<String,Integer>();
以下是错误的通配符使用:
List<? extends Number> list = new ArrayList<String>(); //String is not subclass of Number; so error
Comparator<? super String> cmp = new RuleBasedCollator(new Integer(100)); //Integer is not superclass of String
通配符在泛型中可以是有界的也可以是无界的。
1) 无界通配符
所有的参数类型都是无界的通配符类型表示对其类型变量没有任何限制。比如:
ArrayList<?> list = new ArrayList<Long>();
ArrayList<?> list = new ArrayList<String>();
ArrayList<?> list = new ArrayList<Employee>();
2) 有界通配符
有界通配符对参数的类型做了一些限制,通常使用extends、super限制参数的范围。
上界通配符
说太多不好理解,直接上代码
public class GenericsExample<T>
{
public static void main(String[] args)
{
//List of Integers
List<Integer> ints = Arrays.asList(1,2,3,4,5);
System.out.println(sum(ints));
//List of Doubles
List<Double> doubles = Arrays.asList(1.5d,2d,3d);
System.out.println(sum(doubles));
List<String> strings = Arrays.asList("1","2");
//This will give compilation error as :: The method sum(List<? extends Number>) in the
//type GenericsExample<T> is not applicable for the arguments (List<String>)
System.out.println(sum(strings));
}
//Method will accept
private static Number sum (List<? extends Number> numbers){
double s = 0.0;
for (Number n : numbers)
s += n.doubleValue();
return s;
}
}
下界通配符
package test.core;
import java.util.ArrayList;
import java.util.List;
public class GenericsExample<T>
{
public static void main(String[] args)
{
//List of grand children
List<GrandChildClass> grandChildren = new ArrayList<GrandChildClass>();
grandChildren.add(new GrandChildClass());
addGrandChildren(grandChildren);
//List of grand childs
List<ChildClass> childs = new ArrayList<ChildClass>();
childs.add(new GrandChildClass());
addGrandChildren(childs);
//List of grand supers
List<SuperClass> supers = new ArrayList<SuperClass>();
supers.add(new GrandChildClass());
addGrandChildren(supers);
}
public static void addGrandChildren(List<? super GrandChildClass> grandChildren)
{
grandChildren.add(new GrandChildClass());
System.out.println(grandChildren);
}
}
class SuperClass{
}
class ChildClass extends SuperClass{
}
class GrandChildClass extends ChildClass{
}
6. 哪些情况不允许使用泛型
1) 静态字段不能用泛型
public class GenericsExample<T>
{
private static T member; //This is not allowed
}
2) 不能创建T的实例
public class GenericsExample<T>
{
public GenericsExample(){
new T();
}
}
3) 泛型表达式不接受基本类型
final List<int> ids = new ArrayList<>(); //Not allowed
final List<Integer> ids = new ArrayList<>(); //Allowed
4) 泛型类不能继承java.lang.Throwable
public class GenericException<T> extends Exception {}
参考文献
https://howtodoinjava.com/java/generics/complete-java-generics-tutorial/