2.Java泛型指南-泛型擦除、泛型表达式、类型边界、通配符、无限制通配符、上界通配符下界通配符、泛型作用域、类型上限、?类型泛型、构造函数泛型,泛型使用规则

泛型的规则

类型擦除

类擦除案例
jdk1.5之前代码
public class Node{
   private Object obj;

   public Object get(){
       return obj;
   }
   
   public void set(Object obj){
       this.obj=obj;
   }
   
   public static void main(String[] argv){
    
    Student stu=new Student();
    Node  node=new Node();
    node.set(stu);
    Student stu2=(Student)node.get();
   }
}
使用泛型的代码
public class Node<T>{

    private T obj;
    
    public T get(){
        
        return obj;
    }
    
    public void set(T obj){
        this.obj=obj;
    }
    
    public static void main(String[] argv){
    
    Student stu=new Student();
    Node<Student>  node=new Node<>();
    node.set(stu);
    Student stu2=node.get();
  }
}
两个版本生成的.class文件
  public Node();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
  public java.lang.Object get();
    Code:
       0: aload_0
       1: getfield      #2                  // Field obj:Ljava/lang/Object;
       4: areturn
  public void set(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #2                  // Field obj:Ljava/lang/Object;
       5: return
}
public class Node<T> {
  public Node();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
  public T get();
    Code:
       0: aload_0
       1: getfield      #2                  // Field obj:Ljava/lang/Object;
       4: areturn

  public void set(T);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #2                  // Field obj:Ljava/lang/Object;
       5: return
}

可以看到泛型就是在使用泛型代码的时候,将类型信息传递给具体的泛型代码。而经过编译后,生成的.class文件和原始的代码一模一样,就好像传递过来的类型信息又被擦除了一样。

方法擦除案例
public class ArrayAlg {

    public static <T> T getMiddle(T... a) {
        return a[a.length / 2];
    }

    //编译后的实际代码,演示方法擦除
    public static Object getMiddle(Object ... a) {
        return a[a.length / 2];
    }
}
编译器桥接
package com.naixue.vip.p6.bridging;

/**
 * @Description
 * @Author xh 
 * @Date 2020/7/7 11:05
 **/
public class Node<T> {
    public T data;

    public void setData(T data) {
        this.data = data;
    }

    public Node(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
    public static class MyNode extends Node<Integer>{

        public MyNode(Integer data) {
            super(data);
        }

        @Override
        public void setData(Integer data) {
            System.out.println("MyNode.setData");
            super.setData(data);
        }

        //模拟编译器产生的桥接方法
//        public void setData(Object data){
//            setData((Integer)data);
//        }
    }
    public static void main(String[] args) {
        MyNode mn=new MyNode(5);
        Node n=mn;
        //java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
        n.setData("Hello");
        Integer x=mn.data;
        System.out.println(x);
    }
}
堆污染

Heap pollution(堆污染), 指的是当把一个不带泛型的对象赋值给一个带泛型的变量时, 就有可能发生堆污染.

package com.naixue.vip.p6.heappollution;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description 堆污染案例
 * Heap pollution(堆污染), 指的是当把一个不带泛型的对象赋值给一个带泛型的变量时, 就有可能发生堆污染.
 * @Author xh 
 * @Date 2020/7/7 11:24
 **/
public class Pollution {

    public void funa() {
        List intList = new ArrayList<Integer>();
        intList.add(1);
        //堆污染:类型转换异常
//        List<String> strList = intlist;
    }

    @Test
    public void funb() {
        List<Integer> intList = new ArrayList<>();
        intList.add(1);

        List<String> strList = new ArrayList<>();
        strList.add("a");

        //通过泛型做到了转换,编译通过
        List list=intList;
        List<String> lst=list;

        //运行时会发生转换异常
        //java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
        System.out.println(lst.get(0));

    }
}

翻译泛型表达式

Pair<Parson> pair=new Pair<Parson>();
pair.getFirst();

擦除getFirst的返回类型后将返回Object类型。编译器自动插入Employee的强制类型转换。

编译器把这个方法调用翻译为两条虚拟机指令:

  • 对原始方法Pair.getFirst的调用。
  • 将返回的Object类型强制转换为Parson类型。

子类型规则

类型边界

泛型T在最终会擦除为Object类型,只能使用Object的方法

通配符

假设有一种场景,你不知道这个类型是啥,它可以是Object,也可以是其他类那咋办?

这种场景就需要用到通配符

<T extends Parson>
<T super Parson>
<?>
<? extends Parson>
<? super Parson>
无限制通配符

使用原生态类型是很危险的,但是如果不确定或不关心实际的类型参数。那么在Java 1.5之后Java有一种安全的替换方法,称之为无限制的通配符类型(unbounded wildcard type),可以用一个“?”代替,比如Set<?>表示某个类型的集合,可以持有任何集合。

那么无限制通配类型与原生态类型有啥区别呢?原生态类型是可以插入任何类型的元素,但是无限制通配类型的话,不能添加任何元素(null除外)。

Set<?> set=new HashSet<>();
set.add("abc");//编译错误

它的出现归根结底是为了防止破坏集合类型约束条件,并且可以根据需要使用泛型方法或者有限制的通配符类型(bound wildcard type)接口某些限制,提高安全性。

上界通配符

可以扩展为父类,来调用其方法。必须是子类或者本身。

父类Parson
package com.naixue.vip.p6.vo;

/**
 * @Description 人的class
 * @Author xh 
 * @Date 2020/7/6 17:03
 **/
public class Parson {

    private String name;

    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Parson() {
    }

    public Parson(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return name+","+age;
    }
}
子类Man
package com.naixue.vip.p6.vo;

/**
 * @Description 男人的class
 * @Author xh 
 * @Date 2020/7/6 17:03
 **/
public class Man extends Parson{

    private String watch;

    private String car;

    public Man(String name, Integer age, String watch, String car) {
        super(name, age);
        this.watch = watch;
        this.car = car;
    }

    @Override
    public String toString() {
        return super.getName() + "," + super.getAge()+","+watch+","+car;
    }
}
子类Woman
package com.naixue.vip.p6.vo;

/**
 * @Description 女人的class
 * @Author xh 
 * @Date 2020/7/6 17:03
 **/
public class Woman extends Parson {

    private String bag;

    private String lipstick;

    public Woman() {

    }

    public Woman(String name, Integer age, String bag, String lipstick) {
        super(name, age);
        this.bag = bag;
        this.lipstick = lipstick;
    }

    @Override
    public String toString() {
        return super.getName() + "," + super.getAge()
               + "," + bag + "," + lipstick;
    }
}
上界通配符和类型限定
package com.naixue.vip.p6.wildcard;

import com.naixue.vip.p6.classes.Pair;
import com.naixue.vip.p6.vo.Man;
import com.naixue.vip.p6.vo.Parson;
import com.naixue.vip.p6.vo.Woman;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @Description 泛型类型限定,只能使用子类
 * @Author xh 
 * @Date 2020/7/6 19:56
 **/
public class Programmer<T extends Parson> {

    public <T extends Parson> Pair<T> youngAndOld(T[] a) {
        if(a == null || a.length == 0) {
            return null;
        }
        T min = a[0];
        T max = a[1];
        for(int i = 0; i < a.length; i++) {
            if(min.getAge().compareTo(a[i].getAge()) > 0) {
                min = a[i];
            }
            if(max.getAge().compareTo(a[i].getAge()) < 0) {
                max = a[i];
            }
        }
        return new Pair<>(min, max);
    }

    public void count(Collection<Parson> persons) {
        System.out.println(persons.size());
    }

    @Test
    public void countTest() {
        List<Man> manList=new ArrayList<>();
        //违反子类型化原则,编译报错
//        new Programmer().count(manlist);
    }

    public static void main(String[] args) {
        Programmer<Parson> programmer = new Programmer<Parson>();
        Man man=new Man("陈先生",48,"劳力士","幻影");
        Woman woman=new Woman("刘女士",27,"LV","迪奥");
        Man man1=new Man("张先生",32,"浪琴","奥迪");
        Woman woman1=new Woman("吴女士",18,"LV","圣罗兰");
        Parson[] parsons=new Parson[4];
        parsons[0]=man;
        parsons[1]=woman;
        parsons[2]=man1;
        parsons[3]=woman1;

        //符合上界通配符规则
        Pair<Parson> parsonPair = programmer.youngAndOld(parsons);

        //子类型规则,即任何参数化的类型是原生态类型的一个子类型,
        //Programmer<T>是Programmer<Parson>类型的一个子类型,而不是Programmer<Object>的子类型。
//        Programmer<String> programmer1 = new Programmer<String>();
//        Programmer<Object> programmer1 = new Programmer<Object>();
    }
}
多限定
public class Programmer<T extends Parson & Serializable> {
		
}
下界通配符
    public <T> void func(List<? super Man> src) {
        
    }

    @Test
    public void testCopy() {
        Guide programmer = new Guide();
        programmer.func(new ArrayList<Man>());
        programmer.func(new ArrayList<Parson>());
        //违反下界通配符原则,编译不通过
//        programmer.func(new ArrayList<WoMan>());
    }

泛型的作用域

package com.naixue.vip.p6.wildcard;

import com.naixue.vip.p6.vo.Man;
import com.naixue.vip.p6.vo.Parson;
import com.naixue.vip.p6.vo.Woman;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description 泛型的作用域 
 * @Author xh 
 * @Date 2020/7/6 20:54
 **/
public class Guide<T> {
    /**
     * Hardworkinger的T的作用域是整个class,func的T的作用域就是本方法
     * 当上述两个类型参数冲突时,在方法中,方法的T会覆盖类的T,即和普通变量的作用域一样,内部覆盖外部,外部的同名变量是不可见的
     * @param t
     * @param <T>
     */
    public <T> void func(T t) {

    }

    /**
     * 可以定义不同类型泛型来区分作用域
     * @param s
     * @param <S>
     */
    public <S> void fund(S s) {

    }
}

类型上限

package com.naixue.vip.p6.wildcard;

import com.naixue.vip.p6.vo.Man;
import com.naixue.vip.p6.vo.Parson;
import com.naixue.vip.p6.vo.Woman;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description 泛型的类型上限
 * @Author xh 
 * @Date 2020/7/6 20:54
 **/
public class Guide<T> {
    /**
     * <T extends Parson>指定泛型方法的类型参数的上限
     * @param src
     * @param <T>
     * @return
     */
    public <T extends Parson> T funa(List<T> src) {
        return null;
    }

    /**
     * 不能在方法参数中定义上限
     * @param src
     * @param <T>
     */
//    public <T> T funb(List<T extends Parson> src) {
//        return null;
//    }

    public <T> void copy(List<T> dest,List<? extends T> src) {
        for (T t : src) {
            dest.add(t);
        }
    }

    /**
     * 使用?还可以强制避免你对src做不必要的修改,增加的安全性
     * @param src
     * @param <Parson>
     */
    public <Parson> void updateError(List<? extends Parson> src) {
        for (Parson parson : src) {
            //No candidates found for method call t.setAge(1).
//            parson.setage(1);
        }
    }
}

正确使用?的类型

?表示不可修改的类型

package com.naixue.vip.p6.wildcard;

import com.naixue.vip.p6.vo.Man;
import com.naixue.vip.p6.vo.Parson;
import com.naixue.vip.p6.vo.Woman;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description 泛型的作用域 and 类型上限 and 使用?的不可修改类型
 * @Author xh 
 * @Date 2020/7/6 20:54
 **/
public class Guide<T> {

    public <T> void copy(List<T> dest,List<? extends T> src) {
        for (T t : src) {
            dest.add(t);
        }
    }

    /**
     * 使用?还可以强制避免你对src做不必要的修改,增加的安全性
     * @param src
     * @param <Parson>
     */
    public <Parson> void updateError(List<? extends Parson> src) {
        for (Parson parson : src) {
            //No candidates found for method call t.setAge(1).
//            parson.setage(1);
        }
    }

    public static void main(String[] args) {
        Guide<Parson> programmer = new Guide<Parson>();
        Man man=new Man("陈先生",48,"劳力士","幻影");
        Woman woman=new Woman("刘女士",27,"LV","迪奥");
        Man man1=new Man("张先生",32,"浪琴","奥迪");
        Woman woman1=new Woman("吴女士",18,"LV","圣罗兰");
        List<Parson> parsons=new ArrayList<Parson>();
        parsons.add(man);
        parsons.add(woman);
        parsons.add(man1);
        parsons.add(woman1);

        List<Parson> dest=new ArrayList<Parson>();


        programmer.copy(dest,parsons);
        for (Parson parson : dest) {
            System.out.println(parson);
        }
    }
}

构造函数使用泛型

package com.naixue.vip.p6.kv;

/**
 * @Description 构造函数的通用方法使用
 * @Author xh 
 * @Date 2020/7/6 16:51
 **/
public class GenericKV<K,V> {
    private K key;
    private V value;

    /**
     * 构造函数中使用泛型
     * @param key
     * @param value
     */
    public GenericKV(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }

    /**
     * 静态方法中使用泛型
     * @param p1
     * @param p2
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K,V> boolean compare(GenericKV<K,V> p1,GenericKV<K,V> p2){
        return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue());
    }

    public static void main(String[] args) {
        GenericKV<Integer,String> a=new GenericKV<Integer,String>(1, "a");
        GenericKV<Integer,String> b=new GenericKV<Integer,String>(2, "b");
        System.out.println(compare(a, b));
    }
}

使用规范

add时只能向下转型;向上转型要强转;
具有上界的通配符泛型只能get,不能add除null外的对象;
具有下界的通配符泛型可以add,但get获取对象为object类型;

package com.nx.qiuping.vip.generic.wildcard;

import com.nx.qiuping.vip.generic.vo.Jason;
import com.nx.qiuping.vip.generic.vo.Man;
import com.nx.qiuping.vip.generic.vo.Person;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description 泛型上限定通配符和下限定通配符案例
 * @Author xh 
 * @Date 2020/7/11 18:38
 **/
public class Limited {
    public static  <T> void funa(List<? extends Man> src) {

    }

    public static  <T> void funb(List<? super Man> src) {

    }

    public static  <T> Integer func(List<? super Man> src) {
        return src.size();
    }

    public void test() {
        Limited.funa(new ArrayList<Man>());
        //上界通配符
        //限定参数只能是Man的子类和本身
//        Limited.funa(new ArrayList<Person>());
//        Limited.funa(new ArrayList<Woman>());

        //下界通配符
        //限定参数只能是Man的父类和本身
        Limited.funb(new ArrayList<Man>());
        Limited.funb(new ArrayList<Person>());
//        Limited.funb(new ArrayList<Woman>());

        /**
         * 上界的list只能get,不能add(确切地说不能add出除null之外的对象,包括Object)。
         * 下界的list只能add,不能get。
         */
        List<? extends Person> flistTop = new ArrayList<Person>();
        flistTop.add(null);
        //上界add 对象会报错
        //add无法确定add是哪个子类,所以不允许add
//        flistTop.add(new Man());
//        flistTop.add(new Woman());
//        flistTop.add(new Person());

        //子类直接可以赋值给父类,所以可以get
        Person fruit2 =new Man();
        Person fruit1 = flistTop.get(0);

        //下界
        List<? super Man> flistBottem = new ArrayList<Man>();
        flistBottem.add(new Man());
        flistBottem.add(new Jason());
        //因为父类不能直接赋值给子类所以不能add
//        Man man=new Person();
//        flistBottem.add(new Woman());

        //get的对象是? super Man 类型,模糊类型的所以不能直接复制给Man
//        Man man3=flistBottem.get(0);
        //强转就可以了
        Man man4=(Man) flistBottem.get(0);

    }
}

Java泛型转换的事实

  • 虚拟机中没有泛型,只有普通的类和方法。
  • 所有的类型参数都用它们的限定类型替换。
  • 桥方法被合成来保持多态。
  • 为保持类型安全性,必要时插入强制类型转换。

jdk定义了7种泛型的使用限制

  • 不能用简单类型来实例化泛型实例
  • 不能直接创建类型参数实例
  • 不能申明静态属性为泛型的类型参数
  • 不能对参数化类型使用cast或instanceof
  • 不能创建数组泛型
  • 不能create、catch、throw参数化类型对象
  • 重载的方法里不能有两个相同的原始类型的方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xianghan收藏册

极简精品作,一分也是一份鼓励哦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值