java泛型归纳

之前分享会做的泛型总结

泛型类

1
2
3
4
5
6
7
8
9
10
11
12
public class Generic<T> {
//key这个成员变量的类型为T,T的类型由外部指定
private T key;

public Generic(T key) { //构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}

public T getKey() { //方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}

< >里可以有多个参数

泛型接口

定义一个泛型接口

1
2
3
public interface Generator<T> {
public T next();
}

传入泛型实参时:
定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator
但是我们可以为T传入无数种实参,形成无数种类型的Generator接口
在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型

即:Generator,public T next();中的的T都要替换成传入的String类型

1
2
3
4
5
6
7
8
9
10
public class FruitGenerator implements Generator<String> {

private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}

泛型方法

不要混淆返回值为泛型的方法和泛型方法,二者不同
一个真正的泛型方法:
首先在public与返回值之间的必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
这个T可以出现在这个泛型方法的任意位置
泛型的数量也可以为任意多个,如:

1
2
3
public <T, K> K showKeyName(Generic<T> container) {
//something
}
1
2
3
4
5
public <T> T showKeyName(Generic<T> container) {
System.out.println("container key :" + container.getKey());
T test = container.getKey();
return test;
}

类中也可以有泛型方法

1
2
3
4
5
6
7
8
9
10
public class Test1<T> {

public void testMethod(T t) {
System.out.println(t.getClass().getName());
}

public <T> T testMethod1(T t) {
return t;
}
}

泛型类中的类型参数T与泛型方法中的类型参数T是没有联系的,泛型方法始终以自己定义的类型参数为准

如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法

1
2
3
4
5
6
7
8
9
10
11
public class StaticGenerator<T> {
/**
* 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
* 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
* 如:public static void show(T t){..},此时编译器会提示错误信息:
* "StaticGenerator cannot be refrenced from static context"
*/
public static <T> void show(T t) {

}
}

通配符

<?>

  • 被称作无限定的通配符
  • 当<?>存在时,Collection对象失去add( )能力
  • 即不能调用与类型有关的方法

<? extends T>

  • 被称作有上限的通配符,以T为上界
  • 对应集合丧失add( )能力
  • 可以通过get( )返回元素,能用T以及T的父类接收
  • 只能被T以及T就子类的集合赋值

<? super T>

  • 被称作有下限的通配符,以T为下界
  • 对应集合可以add( )T以及T的子类
  • 可以通过get( )返回元素,但只能用Object接收
  • 只能被T以及T的父类的集合赋值

泛型的上下边界添加,必须与泛型的声明在一起

泛型的声明:即在类名后的< >,泛型方法修饰符和返回值之间的< >

类型擦除

1
2
3
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass()==l2.getClass()); //true

类型擦除:泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉

在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如 则会被转译成普通的 Object 类型,如果指定了上限如 则类型参数就被替换成类型上限String,并不存在的写法

List 一旦传入类型参数 List 就不能add(string)了,但基于对类型擦除的了解,其实T都是Object,可以利用反射绕开这个限制

1
2
3
public interface List<E> extends Collection<E> {
boolean add(E e);
}

类型擦除后,add()方法等同于 boolean add(Object obj);

1
2
3
4
5
6
7
8
9
10
List<Integer> ls = new ArrayList<>();
ls.add(23);
//ls.add("text");
try {
Method method = ls.getClass().getDeclaredMethod("add", Object.class);
method.invoke(ls, "test");
method.invoke(ls, 42.9f);
} catch (Exception e) {
e.printStackTrace();
}

可以看到,利用类型擦除的原理,用反射的手段就绕过了正常开发中编译器不允许的操作限制

参考资料:
https://blog.csdn.net/s10461/article/details/53941091

0%