Java个人对泛型的理解(一)

2 篇文章 0 订阅

Java中的泛型<T>

作用: 是将运行时的异常转到编译时来处理,代码更健壮,更简洁,更灵活,复用性强

AIShoes<T> ; T为类型参数

 AIShoes<Nike>中的Nike为实际类型参数

 AIShoes<T>: 这一整个为泛型类型

 AIShoes<Nike>: 整个称为参数化的类型也就是ParameterizedType

用法: 作用在接口之上,比如一个鞋子接口


public interface IShoes<T> {
}

作用在方法之上

    public <T> AIShoes<T> getShoes(){
        return new AIShoes<T>();
    }

作用在类之上

public class AIShoes<T> {
    T t;

    @Override
    public void setShoesType(T t) {
        this.t = t;
    }

    @Override
    public T getShoesType() {
        return t;
    }
}

 

泛型的擦除

原理: 泛型是JDK5引入的,虚拟机其实是不支持泛型的,所以java实现的是一种伪泛型机制. 为了向下兼容,虚拟机在编译期就会做擦除泛型的操作,这样java就不需要产生新的字节码,所以在java运行时根本就不存在泛型的信息.

泛型是如何擦除的

当一个泛型类编译成字解码后,会将这个<T> 给擦除掉

就拿上面的例子来说

public class AIShoes<T> implements IShoes<T> {
    T t;

    @Override
    public void setShoesType(T t) {
        this.t = t;
    }

    @Override
    public T getShoesType() {
        return t;
    }
}

这个类,使用AMS工具查看擦除后是什么样的

// class version 51.0 (51)
// access flags 0x21
// signature <T:Ljava/lang/Object;>Ljava/lang/Object;Lcom/ancely/fanxing/demo2/IShoes<TT;>;
// declaration: com/ancely/fanxing/demo2/AIShoes<T> implements com.ancely.fanxing.demo2.IShoes<T>
public class com/ancely/fanxing/demo2/AIShoes implements com/ancely/fanxing/demo2/IShoes  {

  // compiled from: AIShoes.java

  // access flags 0x0
  // signature TT;
  // declaration: T
  Ljava/lang/Object; t

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/AIShoes; L0 L1 0
    // signature Lcom/ancely/fanxing/demo2/AIShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.AIShoes<T>
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1
  // signature (TT;)V
  // declaration: void setShoesType(T)
  public setShoesType(Ljava/lang/Object;)V
   L0
    LINENUMBER 8 L0
    ALOAD 0
    ALOAD 1
    PUTFIELD com/ancely/fanxing/demo2/AIShoes.t : Ljava/lang/Object;
   L1
    LINENUMBER 9 L1
    RETURN
   L2
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/AIShoes; L0 L2 0
    // signature Lcom/ancely/fanxing/demo2/AIShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.AIShoes<T>
    LOCALVARIABLE t Ljava/lang/Object; L0 L2 1
    // signature TT;
    // declaration: T
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x1
  // signature ()TT;
  // declaration: T getShoesType()
  public getShoesType()Ljava/lang/Object;
   L0
    LINENUMBER 13 L0
    ALOAD 0
    GETFIELD com/ancely/fanxing/demo2/AIShoes.t : Ljava/lang/Object;
    ARETURN
   L1
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/AIShoes; L0 L1 0
    // signature Lcom/ancely/fanxing/demo2/AIShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.AIShoes<T>
    MAXSTACK = 1
    MAXLOCALS = 1
}

很明显,将T 改成了Object

再来看下面的例子,使用的是T extends Shoes>

public class NikeShoes<T extends Shoes>  {
    public void setShoesType(T t) {
        
    }

    public T getShoesType() {
        return null;
    }
}

编译后

// class version 51.0 (51)
// access flags 0x21
// signature <T:Lcom/ancely/fanxing/demo2/Shoes;>Ljava/lang/Object;
// declaration: com/ancely/fanxing/demo2/NikeShoes<T extends com.ancely.fanxing.demo2.Shoes>
public class com/ancely/fanxing/demo2/NikeShoes {

  // compiled from: NikeShoes.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/NikeShoes; L0 L1 0
    // signature Lcom/ancely/fanxing/demo2/NikeShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.NikeShoes<T>
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1
  // signature (TT;)V
  // declaration: void setShoesType(T)
  public setShoesType(Lcom/ancely/fanxing/demo2/Shoes;)V
   L0
    LINENUMBER 6 L0
    RETURN
   L1
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/NikeShoes; L0 L1 0
    // signature Lcom/ancely/fanxing/demo2/NikeShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.NikeShoes<T>
    LOCALVARIABLE t Lcom/ancely/fanxing/demo2/Shoes; L0 L1 1
    // signature TT;
    // declaration: T
    MAXSTACK = 0
    MAXLOCALS = 2

  // access flags 0x1
  // signature ()TT;
  // declaration: T getShoesType()
  public getShoesType()Lcom/ancely/fanxing/demo2/Shoes;
   L0
    LINENUMBER 9 L0
    ACONST_NULL
    ARETURN
   L1
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/NikeShoes; L0 L1 0
    // signature Lcom/ancely/fanxing/demo2/NikeShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.NikeShoes<T>
    MAXSTACK = 1
    MAXLOCALS = 1
}

很明显擦除后,这个T变成了Shoes了

 

总结: 擦除总共做了三件事; 1: 判断泛型有没有限制,没有限制则转为Object. 2: 如果有限制则转为相对应的类型; 3:有限制并实现了一个接口的话,会

  public synthetic bridge setShoesType(Ljava/lang/Object;)V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    ALOAD 1
    CHECKCAST com/ancely/fanxing/demo2/Shoes//这句是强转的意思
    INVOKEVIRTUAL com/ancely/fanxing/demo2/NikeShoes.setShoesType //这句是调用了setShoesType方法(Lcom/ancely/fanxing/demo2/Shoes;)V
    RETURN
   L1
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/NikeShoes; L0 L1 0
    // signature Lcom/ancely/fanxing/demo2/NikeShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.NikeShoes<T>
    MAXSTACK = 2
    MAXLOCALS = 2
}

多生成一个bridge方法在这个bridge方法中再调用相应的set方法.

为什么会多了一个set方法呢,是因为一个接口在擦除泛型的时候,泛型没有限制的时候会变成Object,而一个类去实现这个接口的时候,因为这个类的泛型有限制,所以在擦除时不会是Object类型, 而在实现接口又一定要实现里面的方法,所以需要一个Object类型的参数,所以就会多出一个带Object参数的bridge方法.

还有一点,擦除完了的类会保存在类的常量池中,所以我们可以通过相应API获取到泛型的类型.(File.getGenericType).

泛型的进阶使用

    public static  <T> void appendS(List<T> list){
        T t = new T();
        list.add(t);
    }

比如上面这个泛型方法,这样在代码里面肯定是报错的,有什么方法可以在里面直接new一个T出来呢,就可以用下面这种方法

    public static <R> void append(List<R> list,Class<R> clazz){
        R r = null;
        try {
            r = clazz.newInstance();
            list.add(r);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

面试题说法:

泛型是为了把运行时的异常移到编译时来处理,它还能使代码更具健壮,灵活,简洁,复用性,它是jdk1.5引入的,jvm虚拟机是不支持泛型的,所以为了兼容,编译的时候就会做泛型擦除的操作.如果泛型没有任何通配符限制的话最终都是转成Object.而通配符又分为非限定通配符和限定通配符 ,非限定的话是用一个? 代表, 限定通配符又分为二种,一个管上限 ? super T; 一个管下限 ? extends T .

? super T 代表的是T 和他的父类及以上  它可以存,不能取,可以称为消费者

? extends T 则代表的是T 和T的子类及以下,它只能取数据不能存,可以称为生产者

打个比放  E extends D    D extends C    C extends B    B extends A

那么List<? extends B> list1 ; 这个list1它只能获取数据不能添加数据, 为什么称为和产者, 是因为他可以不断的获取数据给消费者用.

而List<? super B> list2; 这个list2他可以添加数据,但是他不能取数据,为什么称为消费者,是因为他拿到数据就添加

比如Collections里面的copy方法他的前二个参数: 第一个为super, 第二个extends 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值