1. 为什么我们需要泛型
- 可以用于多种数据类型执行相同的代码
- 可以帮助指定类型,在编译期间就发现错误,避免强制类型转换
2. 泛型类、泛型接口和泛型方法
2.1 泛型类
public class NormalGeneric<T>{
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
public class NormalGeneric2<T, K>{
private T data;
private K instance;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
2.2 泛型接口
pubilc interface Genertor<T>{
public T next();
}
//1. 实现类,可以继续使用泛型
public class ImplGenertor<T> implements Genertor<T>{
@Override
public T next(){
return null;
}
}
//2. 实现类,可以直接指定类型
public class ImplGenertor2 implements Genertor<String>{
@Override
public String next(){
return null;
}
}
2.3 泛型方法
泛型方法没有规定一定要声明在泛型类/泛型接口中。
public class GenericMethod{
//<T>是必须的,且可以是多个,如<T, K>,没有则不是泛型方法,但没有不代表会报错
public <T> T genericMethod(T...a){
return a[a.length/2];
}
public static void main(String[] args){
GenericMethod test = new GenericMethod();
System.out.println(test.<String>genericMethod("mark","andy","simon"));
// <String>可省略
System.out.println(test.genericMethod(1, 3, 5));
}
//不是泛型方法,只是使用了NormalGeneric<Number>这个泛型类作为形参而已
public void show(NormalGeneric<Number> obj){
}
//会报错!!!,因为E泛型未声明过
public <T> T show(E obj){
}
//会报错!!!,因为show所在的类不是泛型类,本身show也不是泛型方法,T泛型未声明过
public void show(T obj){
}
}
public class GenericMethod3{
static class Fruit{
@Override
public String toString(){
return "fruit";
}
}
static class Apple extends Fruit{
@Override
public String toString(){
return "apple";
}
}
static class Person{
@Override
public String toString(){
return "Person";
}
}
static class GenerateTest<T>{
//泛型类的T只影响到这个普通方法
public void show_1(T t){
System.out.println(t.toString());
}
public <E> void show_2(E t){
System.out.println(t.toString());
}
//此T与泛型类的T无关
public <T> void show_3(T t){
System.out.println(t.toString());
}
}
public static void main(String[] args){
Apple apple = new Apple();
Person person = new Person();
GenerateTest<Fruit> generate = new GenerateTest<>();
generate.show_1(apple); //不会报错,因为Apple是Fruit子类,泛型正是为了限制类型的
generate.show_1(person); //报错 !!!
generate.show_2(apple); //不报错
generate.show_2(person); //不报错,因为show_2是泛型方法,与泛型类的泛型无关
generate.show_3(person); //不报错,同上
}
}
3. 泛型的变量类型限定
public class ArrayAlg{
public static <T> T min(T a, T b){
if(a.compareTo(b) > 0) return a; else return b;
}
//如何保证传入的a,b都是有compareTo方法的呢?用泛型!
//Comparable可以是类或接口,且可以是多个,如果类和接口混用,类要放在第一位,因为java是单继承多实现
//限定了T必须是Comparable子类,或者实现了Comparable接口
public static <T,K extends ArrayList&Comparable> T min2(T a, T b){
if(a.compareTo(b) > 0) return a; else return b;
}
}
- 类型限定在泛型类、泛型接口、泛型方法中都可以使用
- 类型限定可以有多个,但如果接口和类混用,类要放第一个,因为java是单继承多实现
4. 泛型使用中的约束和局限性
public class Restrict<T>{
private T data;
public Restrict(){
//不能实例化类型变量
this.data = new T(); //报错!!!
}
//静态域或者静态方法里不能引用类型变量
private static T instance; //报错!!!
//静态方法 本身是泛型方法就行
private static <T> void getInstance(T obj){} //不报错
public void static main(String[] args){
Restrict<double> test = new Restrict(); //报错!!!,基本类型不行,因为他不算是对象,只能用包装类型
Restrict<Double> restrict = new Restrict(); //不报错
Restrict<String> restrictStr = new Restrict(); //不报错
if(restrict instanceof Restric<Double>) //报错!!!不支持instanceof
if(restrict instanceof Restric<T>) //报错!!!不支持instanceof
//getClass().getName()就是包名.Restrict,泛型类型被擦除了
System.out.println(restrict.getClass() == restrictStr.getClass());
Restrict<Double>[] restrictArray;
Restrict<Double>[] restrictArray2 = new Restrict<Double>[10]; //会报错!!!
private class Problem<T> extends Exception{} //会报错!!!
public <T extends Throwable> void doWork(T t){
try{
}catch(T x){ //会报错!!! 不能捕获泛型类对象
}
}
public <T extends Throwable> void doWorkSuccess(T x) throws T{
try{
}catch(Throwable e){ //不会报错
throw x;
}
}
}
}
约束和局限性
- 不能实例化类型变量
- 静态域或者静态方法里不能引用类型变量
原因:泛型的类型在对象创建时才知道,而static先于构造函数执行,而此时泛型的类型是未知的 - 静态方法 本身是泛型方法就行
- 泛型类型不支持instanceof
- getClass获得的只会是原生类型,泛型类型会被擦除
- 可以声明泛型类的数组,但是不能初始化
- 泛型类不能extends Exception/Throwable
- 不能捕获泛型类对象
5. 泛型类型的继承规则
public class Employee{
private String firstName;
private String secondName;
...setter..getter...
}
public class Work extends Employee{}
public class Pair<T>{
private T one;
private T two;
...setter...getter...
private static <T> void set(Pair<Employee> p){
}
public static void main(String[] args){
//Pair<Employee>和 Pair<Worker>没有任何继承关系
Pair<Employee> employeePair = new Pair<>();
Pair<Worker> workerPair = new Pair<>();
Employee employee = new Worker(); //多态,父类句柄 = 子类实例
Pair<Employee> workerPair2 = new Pair<Worker>(); //报错!!!无继承关系
Pair<Employee> pair = new ExtendPair<>(); //不报错,父类句柄 = 子类实例
List<String> list = new ArrayList(); //不报错,经典例子
set(employeePair); //不报错
set(workerPair); //报错!!!,因为employeePair和workerPair没有继承关系,
//如果想解决,使用通配符
}
//泛型类 可以继承或者扩展其他泛型类,比如List和ArrayList
private static class ExtendPair<T> extends Pair<T>{
}
}
6. 泛型中通配符类型
public class Food{}
public class Fruit extends Food{
private String color;
...setter...getter...
}
public class Orange extends Fruit{}
public class Apple extends Fruit{}
public class HongFuShi extends Apple{}
public class GenericType<T>{
private T data
...setter...getter...
}
public class WildChar{
public static void print(GenericType<Fruit> p){
System.out.println(p.getData().getColor());
}
public static void use(){
GenericType<Fruit> a = new GenericType<>();
print(a); //不报错
GenericType<Orange> b = new GenericType<>();
print(b); //报错!!!a和b没有继承关系
}
//<? extends Fruit> 表示类型的上界是Fruit,子类随便,不能是Fruit的父类了
public static void print2(GenericType<? extends Fruit> p){
System.out.println(p.getData().getColor());
}
public static void use2(){
GenericType<Fruit> a = new GenericType<>();
print2(a); //不报错
GenericType<Orange> b = new GenericType<>();
print2(b); //不报错,因为泛型可以是Fruit的子类了
print2(new GenericType<Food>()); //报错!!!Food超出上界
GenericType<? extends Fruit> c = a; //不报错
GenericType<? extends Fruit> c = b; //不报错
Apple apple = new Apple();
Fruit fruit = new Fruit();
//因为规定了上界,所以设置进的只知道一定是Fruit或Fruit的子类,但不能确定具体类型,所以为了安全访问数据,set会报错
c.setData(apple); //报错!!!
c.setData(fruit); //报错!!!
//因为规定了上界,所以取出来的一定是Fruit或Fruit的子类,虽然不确定是哪个子类,但一定能用父类的对象表示,所以可以用Fruit接收
Fruit x = c.getData();
Orange x = c.getData(); //报错!!!
Apple x = c.getData(); //报错!!!
}
//<? super Fruit> 表示类型的下界是Fruit,父类随便,不能是Apple的子类
public static void printSuper(GenericType<? super Apple> p){
System.out.println(p.getData());
}
public static void useSuper(){
GenericType<Fruit> fruitType = new GenericType<>();
GenericType<Apple> appleType = new GenericType<>();
GenericType<HongFuShi> hongFuShiType = new GenericType<>();
GenericType<Orange> orangeType = new GenericType<>();
printSuper(fruitType); //不报错
printSuper(appleType); //不报错
printSuper(hongFuShiType); //报错!!!超越了下界
printSuper(orangeType); //报错!!!Orange和Apple是同级,没有继承关系
GenericType<? super Apple> x = new GenericType<>();
x.setData(new Apple()); //不报错
x.setData(new HongFuShi()); //不报错
x.setData(new Fruit()); //报错!!!
//TODO 重点!!!<? super Apple>时,set只能用Fruit或其子类,不能用父类
//原因:HongFuShi 继承 Apple 继承 Fruit 继承 Food,
//x的类型是Apple或者Fruit或者Food,但不能确定,对于编译器来说,只有当x类型传Apple或其子类时,才能安全的转型为Apple
//所以,为了类型安全,set的时候,只能传Apple和Apple的子类
Object obj = x.getData(); //不报错,返回Apple的超类,但是具体哪个不知道,只能返回Object类型
}
}
7. 虚拟机是如何实现泛型的?
伪泛型
类型擦除
public class GenericRaw<T>{
private T data;
public T getData(){
return data;
}
public void setData(T data){
this.data = data;
}
}
//在jdk里,做了泛型擦除之后,我们可以把它看成是
public class GenericRaw<Object>{
private Object data;
public Object getData(){
return data;
}
public void setData(Object data){
this.data = data;
}
}
public class GenericRaw<T extends Comparable&Serializable>{
private T data;
public T getData(){
return data;
}
public void test(){
data.compareTo();
}
public void setData(T data){
this.data = data;
}
}
//擦除后,如果泛型是extends,用原生类型替换,且是第一个
//如果在代码中使用了Comparable,编译期会加入一个强制类型转换
public class GenericRaw<T extends ArrayList&Comparable>{
private ArrayList data;
public ArrayList getData(){
return data;
}
public void test(){
(Comparable)data.compareTo();
}
public void setData(ArrayList data){
this.data = data;
}
}
public class Theory{
public static void main(String[] args){
Map<String, String> map = new HashMap<>();
map.put("mark", "18");
System.out.println(map.get("mark"));
}
}
//编译后,编译器强制加入类型转换
public class Theory{
public static void main(String[] args){
Map<String, String> map = new HashMap<>();
map.put("mark", "18");
System.out.println((String)map.get("mark"));
}
}
public class Conflict{
//还是会报错!!!因为会类型擦除,所以编译不通过
public static void method(List<String> stringList){
System.out.println("List");
}
public static void method(List<Integer> stringList){
System.out.println("Integer");
}
//下面两例,在jdk的编译中是允许的,但是在开发工具里大概率是不允许的,会报错
//jdk的编译实现中有三个判断:1.方法名 2.参数 3.返回值
//开发工具中有两个判断:1.方法名 2.参数
public static String method(List<String> stringList){
System.out.println("List");
return “OK”;
}
public static Integer method(List<Integer> stringList){
System.out.println("Integer");
return 1;
}
}
注:jdk虽然会擦除类型,但编译成的字节码中增加了许多属性,比如Signature,会记录泛型的原始类,称之为弱记忆。












网友评论