泛型

作者: Anwfly | 来源:发表于2020-07-24 00:06 被阅读0次

一、泛型概述

1. 引入

ArrayList存储字符串并遍历:

ArrayList list = new ArrayList();
list.add("java");
list.add("hello");
list.add(10);//自动

Iterator iterator = list.iterator();
while (iterator.hasNext()) {
      String str = (String) iterator.next();
      System.out.println(str);
}

我们按照正常的写法写这个程序,结果出错了,为什么?
因为我们在存储的时候,存储了string和Integer两种类型的数据,而我们遍历的时候,把他都当做String来处理,做了转化,所以报错,但是没有在编译期间告诉我们。

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at com.anfly.Test.fanxing(Test.java:64)
    at com.anfly.Test.main(Test.java:51)

我们可以回忆一下数组,创建数组对象的时候就明确了元素的类型,集合也可以模仿这种做法,这种技术被称为:泛型

2. 什么是泛型呢

一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊类型,简称参数化类型。

3. 格式

<数据类型>,这里的类型只能是引用数据类型

4. 好处

①把运行时期的问题提前到了编译期间
②避免了强制类型转换
③适用于多种数据类型执行相同的代码

二、泛型分类

1. 泛型类

格式:public class 类名<泛型类型1,…>
注意:泛型类型必须是引用类型

public class FanXingDemo<T> {
    public void show(T t){
        System.out.println(t);
    }
}

测试类

public static void main(String[] args) {    
    FanXingDemo fd1 = new FanXingDemo();
    fd1.show("java");
    fd1.show(1);
    //创建对象时    不指定泛型   调用方法 可以传入任何类型  
    //但是如果在方法中做类型转换 有可能出现类型转换异常
    FanXingDemo<String> fd2 = new FanXingDemo<>();
    fd2.show("hello"); //指定泛型 在调用方法时  只能 传入 指定的泛型类型     
}

2. 泛型方法

格式:public <泛型类型> 返回类型 方法名(泛型类型 .)

public<T> void show(T t){
    System.out.println(t);
}

测试类

FanXingDemo fd1 = new FanXingDemo();
fd1.show("java");
fd1.show(1);
//可以传入  任意数据类型 去 替换  泛型方法上的T

3. 泛型接口

格式:public interface 接口名<泛型类型1…>{}
接口

public interface Test<T> {
    void show(T t);
}

实现方式一,不指定类型

public class Demo<T> implements Test<T> {
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

实现方式二,指定类型

public class Demo implements Test<String> {
    @Override
    public void show(String s) {
        System.out.println(s);
    }
}

测试类

public static void main(String[] args) {

    //创建对象时  不指定  泛型   可以传入任意数据类型
    //可能 出现  类型转换异常
    Demo d = new Demo<>();
    d.show("hello");
    d.show(1);

    //创建对象时  指定  泛型     只能传入指定的数据类型
    Demo<String> d2 = new Demo<>();
    d2.show("hello");
}

三、限定泛型类型变量

1.对类的限定:public class TypeLimitForClass<T extends List & Serializable>{}

public class TypeLimitForClass<T extends List & Serializable> {
    private T data;

    public T getData() {
        return data;
    }

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

    public static void main(String[] args) {
        ArrayList<String> stringArrayList = new ArrayList<>();
        stringArrayList.add("A");
        stringArrayList.add("B");
        ArrayList<Integer> integerArrayList = new ArrayList<>();
        integerArrayList.add(1);
        integerArrayList.add(2);
        integerArrayList.add(3);
        TypeLimitForClass<ArrayList> typeLimitForClass01 = new TypeLimitForClass<>();
        typeLimitForClass01.setData(stringArrayList);
        TypeLimitForClass<ArrayList> typeLimitForClass02 = new TypeLimitForClass<>();
        typeLimitForClass02.setData(integerArrayList);

        System.out.println(getMinListSize(typeLimitForClass01.getData().size(), typeLimitForClass02.getData().size()));

    }

    public static <T extends Comparable<T>> T getMinListSize(T a, T b) {
        return (a.compareTo(b) < 0) ? a : b;
    }
}

2.对方法的限定:public static<T extends Comparable<T>>T getMin(T a, T b) {}

private static <T extends Comparable> T getMin(T a, T b) {
    if (a.compareTo(b) > 0) return a;
    else return b;
}

四、泛型的局限

1.不能用基本类型实例化类型参数,即泛型必须是引用类型。

2.不能实例化类型变量

T t  = new T();

3.静态域或者静态方法里不能引用类型变量

private static T t;

但是静态方法本身是泛型方法就可以

private static <T>  T geInstance(){};

原因分析:不能在静态域或方法中引用类型变量。因为泛型是要在对象创建的时候才知道是什么类型的,而对象创建的代码执行先后顺序是static的部分,然后才是构造函数等等。所以在对象初始化之前static的部分已经执行了,如果你在静态部分引用的泛型,那么毫无疑问虚拟机根本不知道是什么东西,因为这个时候类还没有初始化。

4.无法使用instanceof关键字或==判断泛型类的类型,只有原始类型才可以进行类型判断

if (person instanceof Person<Double>) {}

5.不能捕获泛型类的实例

//泛型类不能extends Exception/Throwable
public class Generic<T> extends Exception

//不能捕获泛型类对象,catch中的T报错
private <T extends Exception> void fun(T t) {
    try {

    } catch (T e) {
    }
}

6.泛型类的原生类型与所传递的泛型无关,无论传递什么类型,原生类是一样的

五、泛型类型的继承规则

//雇员类
public class Employee {
}

//教师类,是雇员的一个子类,他们有继承关系
public class Teacher extends Employee {
}

//定义一个泛型类
public class Pair<T> {
}

Pair<Teacher>和Pair<Employee>有继承关系吗?
//看一下代码,第二行代码报错,说明泛型类不能继承
Employee employee = new Teacher();
Pair<Employee> pair = new Pair<Teacher>();

//但是泛型类可以继承或者扩展其他泛型类,比如List和ArrayList
public class ExtendsPair<T> extends Pair<T> {
}

总结:
①对于泛型参数是继承关系的,泛型类之间是没有继承关系的;
②泛型类可以继承其它泛型类,例如: public class ArrayList<E> extends AbstractList<E>;
③泛型类的继承关系在使用中同样会受到泛型类型的影响。

六、通配符类型

Food.png
  1. <? extends Parent> 指定了泛型类型的上界
  2. <? super Child> 指定了泛型类型的下界
  3. <?> 指定了没有限制的泛型类型
public class GenericByWildcard {
    private static void print(GenericType<Fruit> fruitGenericType) {
        System.out.println(fruitGenericType.getData().getColor());
    }

    private static void use() {
        GenericType<Fruit> fruitGenericType = new GenericType<>();
        print(fruitGenericType);
        GenericType<Orange> orangeGenericType = new GenericType<>();
        //类型不匹配,可以使用<? extends Parent> 来解决
//        print(orangeGenericType);
    }

    /**
     * <? extends Parent> 指定了泛型类型的上届
     */
    private static void printExtends(GenericType<? extends Fruit> GenericType) {
        System.out.println(GenericType.getData().getColor());
    }

    public static void useExtend() {
        GenericType<Fruit> fruitGenericType = new GenericType<>();
        printExtends(fruitGenericType);
        GenericType<Orange> orangeGenericType = new GenericType<>();
        printExtends(orangeGenericType);

        GenericType<Food> foodGenericType = new GenericType<>();
        //Food是Fruit的父类,超过了泛型上届范围,类型不匹配
//        printExtends(foodGenericType);

        //表示GenericType的类型参数的上届是Fruit
        GenericType<? extends Fruit> extendFruitGenericType = new GenericType<>();
        Apple apple = new Apple();
        Fruit fruit = new Fruit();
        /*
         * 道理很简单,? extends X  表示类型的上界,类型参数是X的子类,那么可以肯定的说,
         * get方法返回的一定是个X(不管是X或者X的子类)编译器是可以确定知道的。
         * 但是set方法只知道传入的是个X,至于具体是X的那个子类,不知道。
         * 总结:主要用于安全地访问数据,可以访问X及其子类型,并且不能写入非null的数据。
         */
//        extendFruitGenericType.setData(apple);
//        extendFruitGenericType.setData(fruit);

        fruit = extendFruitGenericType.getData();

    }

    /**
     * <? super Child> 指定了泛型类型的下届
     */
    public static void printSuper(GenericType<? super Apple> GenericType) {
        System.out.println(GenericType.getData());
    }

    public static void useSuper() {
        GenericType<Food> foodGenericType = new GenericType<>();
        printSuper(foodGenericType);

        GenericType<Fruit> fruitGenericType = new GenericType<>();
        printSuper(fruitGenericType);

        GenericType<Apple> appleGenericType = new GenericType<>();
        printSuper(appleGenericType);

        GenericType<HongFuShi> hongFuShiAppleGenericType = new GenericType<>();
        // HongFuShiApple 是Apple的子类,达不到泛型下届,类型不匹配
//        printSuper(hongFuShiAppleGenericType);

        GenericType<Orange> orangeGenericType = new GenericType<>();
        // Orange和Apple是兄弟关系,没有继承关系,类型不匹配
//        printSuper(orangeGenericType);

        //表示GenericType的类型参数的下界是Apple
        GenericType<? super Apple> supperAppleGenericType = new GenericType<>();
        supperAppleGenericType.setData(new Apple());
        supperAppleGenericType.setData(new HongFuShi());
        /*
         * ? super  X  表示类型的下界,类型参数是X的超类(包括X本身),
         * 那么可以肯定的说,get方法返回的一定是个X的超类,那么到底是哪个超类?不知道,
         * 但是可以肯定的说,Object一定是它的超类,所以get方法返回Object。
         * 编译器是可以确定知道的。对于set方法来说,编译器不知道它需要的确切类型,但是X和X的子类可以安全的转型为X。
         * 总结:主要用于安全地写入数据,可以写入X及其子类型。
         */
//        supperAppleGenericType.setData(new Fruit());

        //get方法只会返回一个Object类型的值。
        Object data = supperAppleGenericType.getData();
    }

    /**
     * <?> 指定了没有限定的通配符
     */
    public static void printNonLimit(GenericType<?> GenericType) {
        System.out.println(GenericType.getData());
    }

    public static void useNonLimit() {
        GenericType<Food> foodGenericType = new GenericType<>();
        printNonLimit(foodGenericType);
        GenericType<Fruit> fruitGenericType = new GenericType<>();
        printNonLimit(fruitGenericType);
        GenericType<Apple> appleGenericType = new GenericType<>();
        printNonLimit(appleGenericType);

        GenericType<?> GenericType = new GenericType<>();
        //setData 方法不能被调用, 甚至不能用 Object 调用;
//        GenericType.setData(foodGenericType);
//        GenericType.setData(new Object());
        //返回值只能赋给 Object
        Object object = GenericType.getData();
    }
}

七、虚拟机是如何实现泛型的

Java泛型是Java1.5之后才引入的,为了向下兼容。Java采用了C++完全不同的实现思想。Java中的泛型更多的看起来像是编译期用的
Java中泛型在运行期是不可见的,会被擦除为它的上级类型。如果是没有限定的泛型参数类型,就会被替换为Object.

GenericClass<String> stringGenericClass=new GenericClass<>();
GenericClass<Integer> integerGenericClass=new GenericClass<>();

C++中GenericClass<String>和GenericClass<Integer>是两个不同的类型
Java进行了类型擦除之后统一改为GenericClass<Object>

public class GenericTheory {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("Key", "Value");
        System.out.println(map.get("Key"));
        GenericClass<String, String> genericClass = new GenericClass<>();
        genericClass.put("Key", "Value");
        System.out.println(genericClass.get("Key"));
    }

    public static class GenericClass<K, V> {
        private K key;
        private V value;

        public void put(K key, V value) {
            this.key = key;
            this.value = value;
        }

        public V get(V key) {
            return value;
        }
    }

    /**
     * 类型擦除后GenericClass2<Object>
     * @param <T>
     */
    private class GenericClass2<T> {

    }

    /**
     * 类型擦除后GenericClass3<ArrayList>
     * 当使用到Serializable时会将相应代码强制转换为Serializable
     * @param <T>
     */
    private class GenericClass3<T extends ArrayList & Serializable> {

    }
}

对应的字节码文件

public static void main(String[] args) {
      Map<String, String> map = new HashMap();
      map.put("Key", "Value");
      System.out.println((String)map.get("Key"));
      GenericTheory.GenericClass<String, String> genericClass = new GenericTheory.GenericClass();
      genericClass.put("Key", "Value");
      System.out.println((String)genericClass.get("Key"));
}

八、小试牛刀

  1. 泛型解析JSON数据封装
    ①后台返回的json数据
{
    "code":200,
    "msg":"成功",
    "data":{
        "name":"Jay",
        "email":"10086"
    }
}

②BaseBean

public class BaseBean {
    private int code;
    private String msg;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

③用户数据

public class DataBean<T> extends BaseBean {
    private T data;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
  1. Gson库中的泛型的使用-TypeToken
Gson gson = new Gson();
  List<Person> personList = new ArrayList<>();
  for (int i = 0; i < 5; i++) {
  personList.add(new Person("name" + i, 18 + i));
}
// Serialization
String json = gson.toJson(personList);
System.out.println(json);
// Deserialization
Type personType = new TypeToken<List<Person>>() {
}.getType();
List<Person> personList2 = gson.fromJson(json, personType);
System.out.println(personList2);

相关文章

  • 泛型 & 注解 & Log4J日志组件

    掌握的知识 : 基本用法、泛型擦除、泛型类/泛型方法/泛型接口、泛型关键字、反射泛型(案例) 泛型 概述 : 泛型...

  • 【泛型】通配符与嵌套

    上一篇 【泛型】泛型的作用与定义 1 泛型分类 泛型可以分成泛型类、泛型方法和泛型接口 1.1 泛型类 一个泛型类...

  • 泛型的使用

    泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法 泛型类 泛型接口 泛型通配符 泛型方法 静态方法与...

  • Java 泛型

    泛型类 例如 泛型接口 例如 泛型通配符 泛型方法 类中的泛型方法 泛型方法与可变参数 静态方法与泛型 泛型上下边...

  • 探秘 Java 中的泛型(Generic)

    本文包括:JDK5之前集合对象使用问题泛型的出现泛型应用泛型典型应用自定义泛型——泛型方法自定义泛型——泛型类泛型...

  • Web笔记-基础加强

    泛型高级应用 自定义泛型方法 自定义泛型类 泛型通配符? 泛型的上下限 泛型的定义者和泛型的使用者 泛型的定义者:...

  • 重走安卓进阶路——泛型

    ps.原来的标题 为什么我们需要泛型? 泛型类、泛型接口和泛型方法(泛型类和泛型接口的定义与泛型方法辨析); 如何...

  • Kotlin泛型的高级特性(六)

    泛型的高级特性1、泛型实化2、泛型协变3、泛型逆变 泛型实化 在Java中(JDK1.5之后),泛型功能是通过泛型...

  • Java 19-5.1泛型

    泛型类定义泛型类可以规定传入对象 泛型类 和泛型方法 泛型接口 如果实现类也无法确定泛型 可以在继承类中确定泛型:

  • 【Swift】泛型常见使用

    1、Swift泛型4种 泛型函数泛型类型泛型协议泛型约束 2、泛型约束3种 继承约束:泛型类型 必须 是某个类的子...

网友评论

      本文标题:泛型

      本文链接:https://www.haomeiwen.com/subject/gllllktx.html