zoukankan      html  css  js  c++  java
  • Java——泛型(最易懂的方式讲解泛型)

    来自:
    代码大湿
    代码大湿

    写在前面:

    只要认真看过,基本能很熟悉泛型的特性。泛型是JDK1.5之后出现的,比如JDK1.5之前的ArrayList,会出现2个问题

    1:向ArrayList当中添加对象,添加String和Date都可以,但我们的本意是添加String,编译器不会检查错误,会导致不可预知的错误。

    2:get()方法得到一个元素的时候要进行强制类型转换。
    所以泛型的引入很好的解决了这2个问题。


    1,泛型类

    一个泛型类如下;

    
    class Pair<T,E>{
    	private T first;
    	private E second;
    	public T getFirst() {
    		return first;
    	}
    	public void setFirst(T first) {
    		this.first = first;
    	}
    	public E getSecond() {
    		return second;
    	}
    	public void setSecond(E second) {
    		this.second = second;
    	}
    	
    }
    

    尖括号中的T,E称为类型变量。


    1. 泛型方法

    一个泛型方法的签名:
    public static T getV(T t)
    类型变量在方法修饰符的后面,返回值的前面。


    3.类型擦除

    JVM中是不存在泛型这一说法的,即编译器在编译的时候,将类型变量擦除掉了,换成了Bounding Type。上面的Pair类在编译后,Pair变成这样了:

    class Pair{
    	private Object first;
    	private Object second;
    	public Object getFirst() {
    		return first;
    	}
    	public void setFirst(Object first) {
    		this.first = first;
    	}
    	public Object getSecond() {
    		return second;
    	}
    	public void setSecond(Object second) {
    		this.second = second;
    	}
    	
    }
    

    将类型变量替代成了Object。更多的类型替代请继续往下阅读;

    类型擦除带来的问题:
    定义一个Pair的子类

    class extendsPair extends Pair<Date,Date>{
    	public void setSecond(Date d){
    		super.setSecond(d);
    		System.out.println("我是字类的方法");
    	}
    }
    

    Pair pair=new extendsPair();
    		pair.setSecond(new Date());
    

    执行结果是:
    我是父类的方法
    我是字类的方法

    为什么会是这样呢?

    因为子类自己定义了一个public void setSecond(Date d)
    但是从父类继承了一个方法是public void setSecond(Object d),这是2个不同的方法。当用父类的引用指向子类的实例,然后调用词方法,对于编译器只会寻找父类的那个方法即public void setSecond(Object d)。而程序员的意图是调用子类的方法public void setSecond(Date d),所以此种情况,编译器会为我们生成一个桥方法

    public void setSecond(Object d){
    	setSecond((Date)d);
    }
    

    所以正如结果中那样,先调用父类的setSecond方法,然后在此方法中调用子类的setSecond方法。


    4.类型限定符

    1.类型限定符,用关键字extends表示子类型限定

    对于下面方法

    public People getName(Pair<People,People> p){
    		return p.getFirst();
    	}
    

    这个方法,我们无法传入Pair<Student,Studnet>类型的参数,此时我们就要用到子类型限定,将此方法改为:

    public People getName(Pair<? extends People,? extends People> p){
    		return p.getFirst();
    	}
    

    用关键字super表示超类型限定,<?>表示无类型限定。比如一个方法getPairs()我们要返回Pair<>[]。其中的元素有Pair<Student>,还有Pair<Scientist>Student和Scientist不存在继承关系。这是就只能用<?>

    2:类型限定注意事项:

    看下面代码

    public People getName(Pair<? extends People,? extends People> p){
    		Student student=new Student();
    		p.setFirst(student);
    		return p.getFirst();
    	}
    

    p.setFirst(student)会报错,因为Pair<? extends People,? extends People>的setFirst方法是

    public void setFirst(<? extends People first) {
    		this.first = first;
    	}
    

    形参是People的某子类,但是编译器不能确定Student是不是这个子类的子类。但是对于get方法,总能将返回类型转型成People类。相应的使用超类型限定,get()方法会报错。


    5:泛型的注意事项:

    1:不能在泛型类的静态上下文中定义含有类型变量的静态成员,如:
    
    class Pair<T,E>{
    	private static T name;
    	....
    

    只是对于泛型类来说,如上面普通类中可以存在静态方法。这个原因很简单,比如Pair<String,String>和Pair<Date,Date>这2个对象,那么这两个对象要共享name,那么变量name是什么类型呢?存在冲突。


    2:泛型类不能继承异常类,也不能被抛出
    

    因为泛型类不能继承Throwable,但是类型变量可以被抛出,如:

    public static <T extends Throwable> T get(T t) throws T{
    		return t;
    	}
    

    3:泛型数组是不合法的
    

    不能创建这样的数组:

    Pair<String,String>[] pairs=new Pair<String,String>[10];
    
    

    因为实际上paris是Pair[]类型的,所以我们添加Date类型的元素,编译器是不会发现的。这会产生难以定位的错误。但是我们可以用下面的方式来定义泛型数组。

    Pair<String,String>[] pairs=(Pair<String, String>[])(new Pair[10]);
    	
    
    4:不能实例化类型变量
    

    不能出现 new T(),new T[]这样的src。因为经过类型擦除后,T均变为BoundingType。这样的操作没有意义。

    5:类型变量不能是raw类型。	
    

    关注更多好文:
    代码大湿
    代码大湿

  • 相关阅读:
    MYSQL数据类型——字符串类型
    MYSQL——记录长度
    MYSQL数据类型——时间日期类型
    MYSQL数据类型——数值类型
    为什么在 IDEA jsp 中直接使用 out.println 会出错
    花指令行为大赏
    EasyCpp 题解
    [SUCTF2019] hardcpp 题解
    洛谷 P1650 田忌赛马题解
    Dict 协议是什么
  • 原文地址:https://www.cnblogs.com/pin-wang/p/5554445.html
Copyright © 2011-2022 走看看