Java泛型初识

0、本次学习主要了解了类型擦除。以前都不知道。
1、为什么使用泛型?

        泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用。例如:在Java集合中ArrayList类可以聚集任何类型的对象。

2、泛型的使用

        可以是定义一个泛型类,定义一个泛型方法。泛型方法不仅可以在泛型类中定义也可以在普通类中进行定义。

//定义一个普通的泛型类
public class A<T>{
    private T a;
    private T b;
    public A(){a=null;b=null;}
    public A(T a,T b){this.a=a;this.b=b;}
    
    public T getA(){return a;}
    public T getB(){return b;}

    public void setA(T value){a=value;}
    public void setA(T value){b=value;}
    //、、、、
}
//在普通类中定义一个泛型方法
public class B{
    public static <T>T getNumber(T a){//返回类型和参数类型必须一致
        return a;
    }
}
3、类型变量的限定

        类或方法需要对类型变量加以限制。例如现在我们需要写一个比较大小的方法。利用泛型参数是可以随意的。但是,每一种参数都需要有compareTo()这个方法进行两数比较。如果该类型的参数没有实现Comparable接口,则compareTo()方法无法使用,编译器会报错。所以T应该是绑定类型的子类型。

//类型变量的限定
public class C{
    public static <T extends Comparable & Serializable>T max(T a,T b){//在这里extends并不是继承的意思。它相当于绑定,绑定的可以是类也可以是接口。该例子中的Comparable就是一个接口
        return a.comparreTo(b);
    }
}

若要绑定多个可以接口或者类之间使用符号**&** 进行连接。

4、泛型代码和虚拟机
1、类型擦除

        虚拟机中只有普通的类和方法并没有泛型类型对象。在进行编译的过程中,虚拟机会对泛型程序进行类型擦除。无论何时定义一个泛型类型,都自动提供一个相应的原始类型原始类型 的名字就是删去类型参数后的泛型类型名。擦除 类型变量并替换为限定类型(就是绑定的类型,如果没有那么默认为Object类型)。

2、翻译泛型表达式

        当程序调用泛型方法时,擦除返回类型后,编译器会插入强制类型转换。如果类型变量没有绑定任何类或者接口,那么默认会强制转换为Object类型。

public class A<T>{
    private T a;
    private T b;
    public A(){a=null;b=null;}//参数肯定是一个对象,所以默认值均可被设置为null
    public A(T a,T b){this.a=a;this.b=b;}
    
    public T getA(){return a;}
    public T getB(){return b;}

    public void setA(T value){a=value;}
    public void setA(T value){b=value;}
    
    public static void main(String[]args){
        A<Integer> a=new A<Integer>(1,2);
        /*有两条指令
        1、对原始方法getA()的调用
        2、将放回的Object类型强制转换为Integer类型。又由于Integer可以进行自动拆箱。所以可以赋给基本数据类型int。
        */
        int b=a.getA();
        System.out.println(b);
    }
}
3、翻译泛型方法

        类型擦除也会出现在泛型方法中。例如:

public static <T extends Comparable>T min(T a,T,b){}

经过类型擦除后,T已经被擦出了,只留下了限定类型Comparable。这就会出现问题:如果某一类在继承这个泛型类之后。程序员很有可能对对父类的方法进行重写。其实这里不能算作重写只能算作重载。看下面的例子:

public class D extends A<Integer>{
    public void setA(Integer a){}
}

在类型擦除之后,A类的setA()方法将会变成:

public void setA(Object a){}

这就导致存在两个方法名相同的方法。为了避免,通过使用桥方法 。如下:

public void setA(Object a){setA(Integer a);}

总之,需要记住有关java泛型转换的事实:

  • 虚拟机中没有泛型,只有普通的类和方法;
  • 所有的类型参数都是用它们的限定类型进行替换;
  • 桥方法被合成用来保持多态;
  • 为保持类型安全性,必要时插入强制类型转换;
5、泛型的约束与局限性
  • 不能用基本类型实例化类型参数
            例如:ArrayList list=new ArrayList<>();这是不可行的。java虚拟机进行类型擦除后生成的Object对象不可能存储int类型(可以理解为对象无法强转为基本类型)。虽然不能使用基本数据类型进行声明,但可以使用基本数据类型的包装类。
  • 不能创建参数化类型的数组
            例如:ArrayList[] st=new ArrayList[10];这样声明是错误的。类型擦除后,st的类型是ArrayList[]。数据类型就会丢失。需要说明的是,只是不允许创建这些数组,而声明类型为ArrayList[] st的变量仍然是合法的。不过不能用new ArrayList[10]初始化这个变量。
  • 不能实例化类型变量
            不能实例化对象。例如:上面的A类。a=new T();这样是错误的。类型擦除后将T改成Object。虽然合理,但是没事创建一个Object对象也不是自己的目的。
  • 不能构造泛型数组
            通过类型擦除后,数组的数据类型就定死了(为限定类型或者Object)。但该构造数组可以是私有变量。这样外部类无法调用可以进行类型转换。
  • 泛型类的静态上下文中类型变量无效
  • 不能抛出或捕获泛型类的实例
  • 可以消除对受查异常的检查
6、泛型类型的继承规则

        无论S与T有什么联系,通常ArrayList< S>和 ArrayList < T> 没有什么联系。也可以这么理解,就算S和T存在继承关系,但是ArrayList< S>和ArrayList< T>不存在继承关系。

  • 下面的内容自己看的就比较浅显了。甚至没有看懂。从java核心技术卷一上学习得到的。只是为了让自己能够记住,方便自己以后查找。希望大佬们一笔带过。小白会在自己需要的时刻重新回炉

  • 以下代办。

7、通配符

        带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取。

你可能感兴趣的:(初期学习)