泛型概述:
泛型的本质是参数化类型,也就是说变量的类型是一个参数,在使用时再指定为具体类型
泛型是在编译时被确定
常见通配符:
可以根据使用泛型的位置分为:泛型类,泛型接口,泛型方法
Java 泛型总结(一):基本用法与类型擦除
java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
1.泛型类
泛型类就是最常见的泛型使用方式,即泛型直接用在实体类上,示例代码如下:
// 此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
public class Generic{
// key这个成员变量的类型为T,T的类型由外部指定
// 注:若数组要用泛型时,不能声明成泛型数组E[],而是Object[],然后再通过泛型类型转换
private T key;
public Generic(T key) { // 泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ // 泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
几点注意:
泛型的类型参数只能是类类型(String,Integer),不能是简单类型(int,double)
使用泛型的时候如果传入泛型实参,会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。
public static void main(String[] args) {
// 在声明List时,没有传入指定指定具体类型
List list = new ArrayList<>();
// 可以传入 Integer
list.add(1);
// 还可以传入 String
list.add("abc");
System.out.println(list); // [1,"abc"]
}
若泛型类型已确定,则只能是其本身,其子类不能使用
void f (List a);
void f1() {
List cats = new ArrayList<>()
f(cats) // error
}
不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。
if(ex_num instanceof Generic){
}
2.泛型接口
泛型接口就是泛型用在接口 interface 上,示例代码如下:
public interface Generator {
public T next();
}
这里注意,实现泛型的类,必须传入泛型实参,然后在实现类中用该实参替换T:
class FruitGenerator implements Generator // 实现类还是用T,编译器会报错:"Unknown class"
public class FruitGenerator implements Generator {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
// 此处用string替换T
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
3.泛型方法
泛型不但可用作用于整个类上,同时还可以作用于参数化方法。泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。要定义泛型方法,只需将泛型参数列表置于返回值之前,示例代码如下:
public class GenericMethod {
// 该方法public和返回值之间是泛型T,是一个泛型方法
// 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
public void f(T t){
System.out.println(t.getClass().getName());
}
public static void main(String[] args) {
Method method = new Method();
method.f("123"); // java.lang.String
method.f(111); // java.lang.Integer
}
}
再强调一下,只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。泛型方法可以出现杂任何地方和任何场景中使用,但类中的泛型方法较为特殊
public class GenericFruit {
class Fruit{
@Override
public String toString() {
return "fruit";
}
}
class Apple extends Fruit{
@Override
public String toString() {
return "apple";
}
}
class Person{
@Override
public String toString() {
return "Person";
}
}
class GenerateTest{
public void show_1(T t){
System.out.println(t.toString());
}
// 在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
// 由于泛型方法在声明的时候会声明泛型,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
public void show_3(E t){
System.out.println(t.toString());
}
// 在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
public void show_2(T t){
System.out.println(t.toString());
}
}
public static void main(String[] args) {
Apple apple = new Apple();
Person person = new Person();
GenerateTest generateTest = new GenerateTest();
// apple是Fruit的子类,所以这里可以
generateTest.show_1(apple);
// 编译器会报错,因为泛型类型实参指定的是Fruit,而传入的实参类是Person
// generateTest.show_1(person);
// 使用这两个方法都可以成功
generateTest.show_2(apple);
generateTest.show_2(person);
// 使用这两个方法也都可以成功
generateTest.show_3(apple);
generateTest.show_3(person);
}
}
4.泛型上下边界
在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。
上边界 < ? extends E>
即传入的类型必须是指定类型或指定类型的子类
可以获取,不可以插入
public class test {
public static void main(String[] args) {
List extends Father> list = new LinkedList<>();
list.add(new Son());
}
}
class Human{
}
class Father extends Human{
}
class Son extends Father{
}
class LeiFeng extends Father {
}
// 报错 The method put(Son) is undefined for the type List
List extends Father> list1 = new ArrayList(); // list1可以add Father和所有Father的子类
List extends Father> list2 = new ArrayList(); // list2可以add Son和所有Son的子类
List extends Father> list3 = new ArrayList(); // list3可以add LeiFeng和所有LeiFeng的子类
下边界 < ? super E>
在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类
可以插入,不可以获取
List super Interger> list = null;
list = new ArrayList();
Number num1 = list.get(1); // ×,子类对象的引用无法赋值给兄弟类的引用
Integer num2 = list.get(1); // ×,父类对象的引用无法赋值给子类的引用
list.add(new Integer(1)); // √,子类对象的引用可以赋值给父类对象的引用