唬人的Java泛型并不难

泛型

public interface Foo<E{}

public interface Bar<T{}

public interface Zar<?> {}

上面的代码有什么区别?java

泛型初探

一、为什么引入泛型?web

Java 泛型也是一种语法糖,使用泛型能够在代码编译阶段完成类型的转换,避免代码在运行时强制转换而出现ClassCastException的异常。网络

网络搜索出来一大堆的名称解释,咱们先看英文Generic type,从英文大概也能明白,Generic 这里能够理解为普通的,通常的,或者咱们能够说通用的。 数据结构

其实能够理解为Java中的一种类型,通用类型。app

Java从1.5的版本就开始支持泛型,不过不少小伙伴对泛型仍是模凌两可,今天大概讲讲泛型,基础好的小伙伴,就当复习复习。this

在1.5版本之前编码

public static void main(String[] args){
    List list = new ArrayList();
    list.add("兔子托尼啊");
    list.add(1234);
    //正常运行
    System.out.println((String)list.get(0));
    //❌运行时报错
    System.out.println((String)list.get(1));
}

从上面的代码能够看出了,第一句打印不报错,第二句打印会报错的。spa

List默认是Object的类型的,向List里面存数据都是没有问题的,可是取数据的时候,必需要要进行类型的转换。code

List集合get数据的时候并不清楚里面存放的什么数据类型,默认取出来的都是Object的类型,若是取数据的时候转换的类型和原始存放存的类型不同,会报ClassCastException的异常。orm

二、引入了泛型

看代码

List<String> list = new ArrayList<String>();
list.add("兔子托尼啊");
//❌编译时错误
list.add(1234);
//不须要再进行转换了
String str = list.get(0);

三、泛型带来好处

  • 这在编码的时候就给咱们解决了,类型转换的问题,能够放心写代码。

  • 取数据的时候不再要考虑我前面存的什么类型,我应该转换为何类型,不怕类型转换报错。

类型擦除

上面讲了泛型,泛型虽然带来了好处,可是泛型也带了一个问题叫作类型擦除。

什么是类型擦除?

Java的泛型是伪泛型,这是由于Java在编译期间,全部的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除。

Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。

class GenericU {
    public void foo() {
        System.out.println("GenericU.foo()");
    }
}
public class Operater<T{
    private T obj;
    public Operater(T obj) {
        this.obj = obj;
    }
    public void doIt() {
        //❌报错,提示找不到foo方法
        obj.foo(); 
    }
   public static void main(String[] args) {
        GenericU genericU  = new GenericU();
        Operater<GenericU> operater = new Operater<>(genericU);
        operater.doIt();
    }
}

上面的代码就是由于泛型擦除,带来编译就报错了,代码中的obj不知道是什么类型?

正确的代码应该是什么,只要指定T的类型就好

 class Operater2<T extends GenericU{
    private T obj;
    public Operater2(T obj) 
      this.obj = obj; 
      }
    public void doIt() {
      //正确☑️
      obj.foo(); 
    }
}

区分在Operater2<T extends GenericU>Operater<T>
必须指定泛型的类型。

上面的例子是运用在类上面的,方法中是什么效果呢?

class Foo{
  //定义泛型方法..
  public <T> void show(T t) {
      System.out.println(t);
  }
}

调用方法

public static void main(String[] args) {
    //建立Foo对象
    Foo foo = new Foo();
    //不一样的类型参数
    foo.show("兔子托尼啊");
    foo.show(1234);
    foo.show(12.34);
}

通配符与上下界

咱们你们在java的源码中确定看到这样的例子。一个下限,一个上限

? extends T VS ? super T

  • ? extends T - 这里的?表示类型T的任意子类型,包含类型T自己。
  • ? super T - 这里的?表示类型T的任意父类型,包含类型T自己。

上限通配符 能够表明未知的T类型,或者经过关键字 extends 所继承的T类的任何一个子类。

一样,下限通配符 能够表明未知的T类型,或者经过关键字super出来的的T类的任何一个父类。

通配符和泛型方法

//通配符
public  void foo1(List<?> list) {
}

//使用泛型方法
public <T> void  foo2(List<T> t) {
}

问: 上面两种代码都是能够的,可是什么场合用那种呢?

  • 若是当参数之间有依赖关系,或者返回的参数有依赖关系则用泛型,反之则用通配符。

问:关于 ? extends T? super T 什么场景下用呢?

我从网上搜索了下

当你须要从一个数据结构中获取数据时(get),那么就使用 ? extends T;若是你须要存储数据(put)到一个数据结构时,那么就使用 ? super T; 若是你又想存储数据,又想获取数据,那么就不要使用通配符 ? ,即直接使用具体泛型T。

最后

泛型大概就讲了上面的内容,你看明白了吗?但愿你又学到了,天天学一点,进步一点。升职加薪就是你了。

码字不易,关注后送福利,求关注。

相关文章
相关标签/搜索