JAVA| 泛型知识点全面总结—参考JAVA核心技术 卷一第五版

泛型的定义

  1. 定义泛型类
public class Demo1 {
    public static void main(String[] args) {
        Pair pair = new Pair<>();
        pair.first = 1;
        pair.second = 2;
        pair.third = "3";
    }
}

class Pair {
    public T first;
    public T second;
    public V third;
}

2.定义泛型方法(调用时不用指定类型,除非编译器无法推断出时)

class Methods{
    public static  T getName(T info) {
        return info;
    }
}
Methods methods = new Methods<>();
Object o = Methods.getName(5);
System.out.println(""+ o.getClass().getSimpleName());//输出Integer

当在泛型类中定义泛型方法时 泛型方法应与类的泛型标记不一致,否则编译器会提示警告, 但实际匹配仍以泛型方法运行时检测类型为准

class Methods{
     public   T getName(T info) {
         return info;
     }
 }

泛型的限定

  1. 使用关键词 extends;
  2. 如有多个可使用&分隔,并且传入的参数类型必须同时满足extends后面的类型;
  3. extends后面可以是类,抽象类,接口;
  4. 建议extends后面的类型标签接口(无方法接口)放在最后一个;
class Animal implements Common{
    void speak(){
        System.out.println("speak");
    }

    @Override
    public void who() {
        System.out.println("Animal");
    }
}

interface Common {
    void who();
}

Test.test(new Animal());

泛型代码与虚拟机

类型擦除

  1. java的泛型存在于编译阶段,编译成字节码时无泛型,虚拟机里是没有泛型的;
  2. 泛型类型都会应原始类型,原始类型就是删除类型参数后的泛型类型名,擦除类型变量,并替换成限定类型(无限定的用Object);
  3. 当限定类型有多个的时候,选择第一个类型来替换;

翻译泛型表达式

Pair pair ...
String s = pair.getKey();
-----------------------------
编译器会翻译成
String s = (String) pair.getKey();

编译器会翻译成两条指令:

  1. 对原始方法的调用;
  2. 返回的Object强转为指定类型;

翻译泛型方法

class Child extends Root {
    public void setNum(Integer a) {
        super.setNum(a);
    }
}

class Root {
    private T num;
    public void setNum(T num) {
        this.num = num;
    }
}

Child child = new Child();
Root root = child; //多态
root.setNum(10);

编译器会翻译成

 class Child extends Root {
     public void setNum(Integer a) {
         super.setNum(a);
     }
 }
 
 class Root {
     private Object num;
     public void setNum(Object num) {
         this.num = num;
     }
 }

由于super调用的是重写方法,但重写必须满足以下要求

  • 参数列表必须完全与被重写方法的相同
  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
  • 父类的成员方法只能被它的子类重写。
  • 声明为 final 的方法不能被重写。
  • 声明为 static 的方法不能被重写,但是能够被再次声明。
  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
  • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
  • 构造方法不能被重写。
  • 如果不能继承一个方法,则不能重写这个方法。 

我再补充一下重载

  • 被重载的方法必须改变参数列表(参数个数或类型不一样);
  • 被重载的方法可以改变返回类型;
  • 被重载的方法可以改变访问修饰符;
  • 被重载的方法可以声明新的或更广的检查异常;
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。

重写与重载之间的区别

区别 重载 重写
参数列表 必须修改 不能更改
返回类型 可以修改 不能更改
异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
访问 可以修改 一定不能做更严格的限制(可以降低限制)

所以super无法访问父类的public void setNum(Integer a) 因此,编译器会在子类中自动生成一种桥方法

class Child extends Root {
     public void setNum(Integer a) {
         super.setNum(a);
     }
     public void setNum(Object a) {
         setNum((Integet)a);
     }
/*
不能自定义桥方法 否则编译器报错
//ERROR
  public void setNum(Object a) {
         setNum((Integet)a);
     }
*/
 }

Java泛型转换:

  1. 虚拟机中没有泛型,只有普通的类和方法;
  2. 所有的类型参数都用他们的限定类型替换;
  3. 桥方法的合成是用来保持多态;
  4. 为保证类型安全性,必要时插入强制类型转换;

约束与局限性

1.不能用基本类型实例化类型参数 2.运行时类型查询只适用于原始类型

class Things { }

class Animal { }

class People { }

Things things = new Things<>();
Things peopleThings = new Things<>();
System.out.println(""+things.getClass().getName());// Things
System.out.println(""+peopleThings.getClass().getName());// Things
boolean a = things instanceof Things; // 编译器报错 不允许这样操作
boolean b = things instanceof Things; // 编译器提醒一直为true

3.不能创建参数化类型的数组, 不能构造泛型数组:

Things[] things;// 可以声明 但不能new
Things[] things = (Things[])new Things[10];// 编译器报警告 但不安全

4.可变参数列表中是泛型类型的方法前需加@SafeVarargs

@SafeVarargs
private static void add(List list, T... args) {
    list.addAll(Arrays.asList(args));
}
Things things1 = new Things<>();
Things things2 = new Things<>();
List> list = new ArrayList<>();
add(list, things1, things2);

5.不能实例化类型变量

class Things {
    private T arg1 = new T();// 编译器报错
}

正确使用应该是利用函数接口或者使用反射(需增加try):

class Things {
    private T arg1;
    Things(T arg1) {
        this.arg1 = arg1;
    }
    public void getArg1() {
        System.out.println("type:"+arg1.getClass().getSimpleName()+" v"+arg1);
    }
}

/**
 * @param construct,表示一个无参数而且返回类型为 T 的函数
 */
private static  Things getObject(Supplier construct) {
    return new Things<>(construct.get());
}

/**
 * 使用反射方法 添加Try Catch
 */
private static  Things getObject(Class construct) {
    try {
        return new Things<>(construct.newInstance());
    } catch (InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;
}

Things things = getObject(String:: new);
// 或者
Things things1 = getObject(String.class);
things.getArg1();
assert things1 != null;
things1.getArg1();

6.泛型类的静态上下文中类型变量无效(静态属性不能含有泛型) 考虑Singleton

public class Singleton {
    private static T singlelnstance; // Error
    public static T getSinglelnstanceO // Error
    {
        if (singleinstance == null) construct new instance of T
            return singlelnstance;
    } 
 }

7.不能抛出或捕获泛型类的实例

catch 子句中不能使用类型变量

泛型类扩展 Throwable 都是不合法的

在异常规范中使用类型变量是允许的

// 无法编译
public static  void doWork(Class t) {
    try
    {
        do work
    }
    catch (T e) // Error can 't catch type variable
    {
        Logger,global.info(...);
    }
}
// 可以通过
public static  void doWork(T t) throws T {// OK
    try{
    do work;
    } catch (Throwable realCause){ 
        t.initCause(realCause);
        throw t;
    }
}

8.可以消除对受查异常的检查

就是将所有异常都包装成指定的一个异常抛出

class Block{
    @SuppressWarnings("unchecked")
    public static void throwAs(Throwable e) throws T {
        throw (T) e;
    }  
}
// 将任何异常都指定为RuntimeException
try {
    do work;
}catch (Throwable t)
{
    B1ock.throwAs(t) ;
}

通配符类型

  • 格式: TypeClass
  • 通配符的超类型限定 TypeClass 
  • 无限定通配符 TypeClass

 

你可能感兴趣的:(JAVA)