泛型

1/13/2022 Java

# 什么是泛型

泛型就是在Java程序编译的时候给预定义的类赋予值的类。

# 为什么要使用泛型

首先看以下例子

package com.yuwei;

import java.util.ArrayList;

public class Test1 {
    public static void main(String[] args) {
        ArrayList objects = new ArrayList();
        objects.add("a");
        objects.add(10);
        objects.add(true);
        for (Object object : objects) {
            String tem = (String)object;
            System.out.println(tem);
        }
    }
}

我们知道ArrayList<>()本身是一个泛型类,但是如果不指定相关类型,那么泛型类的默认类型是object类

如果只知道是Object类的话,如上图代码,我们想拿到String类型的数据就需要通过(String)进行强转,这样的话编译正常通过,但是运行的时候就会报错(Integer无法转换成String)。

而如果我们引入了泛型类,提前指定类型,那么就会解决以上问题。 泛型的好处有二:第一,使用泛型规定对应类型,如果赋值的时候给一个不对应的值,那么该类就会编译报错,避免运行时报错。第二,使用泛型,可以更加灵活的规定需要用的数据类型。

用法:

public ClassName <E、K、T.......>

<>中可以放若干个泛型类

如果不指定参数,默认为Object类型。

抽奖实例:

package com.yuwei.price;

import java.util.ArrayList;
import java.util.Random;

public class DrawRaffle<T> {
    private T product;

    private ArrayList<T> productList = new ArrayList<>();

    Random random = new Random();

    public T getProduct() {
        return product;
    }

    public void setProduct(T product) {
        this.product = product;
    }

    public void init(T product){
        productList.add(product);
    }

    public T getTheProduct(){
        return productList.get(random.nextInt(productList.size()));
    }
}
package com.yuwei.price;


public class RaffleTest {
    public static void main(String[] args) {
        DrawRaffle<String> stringDrawRaffle = new DrawRaffle<>();
        String[] productList = {"三国演义","水浒传","红楼梦","西游记"};
        for (String s : productList) {
            stringDrawRaffle.init(s);
        }
        System.out.println("抽到的奖品是:"+stringDrawRaffle.getTheProduct());

        DrawRaffle<Integer> integerDrawRaffle = new DrawRaffle<>();
        Integer[] money = {11,22,333,6666};
        for (Integer integer : money) {
            integerDrawRaffle.init(integer);
        }
        System.out.println("抽到的奖金是:"+integerDrawRaffle.getTheProduct());


    }
}
com.yuwei.price.RaffleTest
抽到的奖品是:红楼梦
抽到的奖金是:11

Process finished with exit code 0

# 泛型子类的应用

  • 如果父类,子类都是泛型,子类的类型必须与父类一致,且子类可以扩展其他泛型参数。
package com.yuwei.test;

public class Parent<T>{
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}
package com.yuwei.test;

public class Child<T,E> extends Parent<T> {

    private E ele;

    @Override
    public T getValue() {
        return super.getValue();
    }

    @Override
    public void setValue(T value) {
        super.setValue(value);
    }

    public E getEle() {
        return ele;
    }

    public void setEle(E ele) {
        this.ele = ele;
    }
}
package com.yuwei.test;

public class Test {
    public static void main(String[] args) {
        Child<String, Integer> stringIntegerChild = new Child<>();
        stringIntegerChild.setValue("张三");
        stringIntegerChild.setEle(12);
        System.out.println(stringIntegerChild.getEle());
        System.out.println(stringIntegerChild.getValue());
    }
}
  • 如果子类不是泛型类,父类是泛型类,子类继承父类时必须要指定父类的类型。
package com.yuwei.test;

public class Child2 extends Parent<Integer>{
    @Override
    public Integer getValue() {
        return super.getValue();
    }

    @Override
    public void setValue(Integer value) {
        super.setValue(value);
    }
}
package com.yuwei.test;

public class Test {
    public static void main(String[] args) {
        Child<String, Integer> stringIntegerChild = new Child<>();
        stringIntegerChild.setValue("张三");
        stringIntegerChild.setEle(12);
        System.out.println(stringIntegerChild.getEle());
        System.out.println(stringIntegerChild.getValue());

        //这时的child2类会变为普通的继承于Parent Integer类型的类。
        Child2 child2 = new Child2();
        child2.setValue(44);
        System.out.println(child2.getValue());
    }
}

# 泛型接口

1.如果实现泛型接口的类是普通类,则必须要指定实现泛型接口的类型,否则默认为Object类型。

package com.yuwei.interfacet;

public interface Generator<T> {
    T getKey();
}
package com.yuwei.interfacet;

//实现的是Stirng类型的接口
public class Apple implements Generator<String>{

    @Override
    public String getKey() {
        return "This is a Apple";
    }
}

2.如果实现泛型接口的类是泛型类,则泛型类与泛型接口必须有一个一样的泛型参数,泛型类可以进行其他扩展

package com.yuwei.interfacet;

public class Pair<T,E> implements Generator<T> {
    T t;
    E e;
    @Override
    public T getKey() {
        return t;
    }

    public E getE(){
        return e;
    }

    public void setKey(T t){
        this.t= t;
    }

    public void setE(E e){
        this.e=e;
    }

}

测试类:

package com.yuwei.test;

public class Test {
    public static void main(String[] args) {
        Child<String, Integer> stringIntegerChild = new Child<>();
        stringIntegerChild.setValue("张三");
        stringIntegerChild.setEle(12);
        System.out.println(stringIntegerChild.getEle());
        System.out.println(stringIntegerChild.getValue());

        Child2 child2 = new Child2();
        child2.setValue(44);
        System.out.println(child2.getValue());
    }
}

输出结果:

This is a Apple
23
hah

# 泛型方法

泛型方法不是泛型成员方法而是泛型的方法

格式:

Public [static] <T> void methodName(){}

package com.yuwei.price;


import java.util.ArrayList;
import java.util.Random;

public class DrawRaffle<T> {
    private T product;

    private ArrayList<T> productList = new ArrayList<>();

    Random random = new Random();

    public T getProduct() {
        return product;
    }

    public void setProduct(T product) {
        this.product = product;
    }

    public void init(T product){
        productList.add(product);
    }

    //泛型类的成员方法
    public T getTheProduct(){
        return productList.get(random.nextInt(productList.size()));
    }

    //泛型方法
    public <E> E getTheProduct(ArrayList<E> e){
        return  e.get(random.nextInt(e.size()));
    }
}

测试类:

package com.yuwei.price;

import java.util.ArrayList;

public class Test2 {
    public static void main(String[] args) {
        DrawRaffle<Object> objectDrawRaffle = new DrawRaffle<>();
        ArrayList<String> strings = new ArrayList<>();
        strings.add("苹果电脑");
        strings.add("游戏机");
        strings.add("手机");
        String theProduct = objectDrawRaffle.getTheProduct(strings);
        System.out.println(theProduct);
        System.out.println("--------------------------------");
        ArrayList<Integer> integers = new ArrayList<>();
        integers.add(1000);
        integers.add(2000);
        integers.add(3000);
        Integer theProduct1 = objectDrawRaffle.getTheProduct(integers);
        System.out.println(theProduct1);
    }
}

输出:

游戏机
--------------------------------
2000

# 类型通配符

package com.yuwei.demo;

public class Box<E> {
    private E cap;

    public E getCap() {
        return cap;
    }

    public void setCap(E cap) {
        this.cap = cap;
    }
}

package com.yuwei.demo;

public class Test01 {
    public static void main(String[] args) {
        Box<Number> numberBox = new Box<>();
        numberBox.setCap(100);
        showBox(numberBox);
    }

    public static void showBox(Box<Number> box) {
        Number cap = box.getCap();
        System.out.println(cap);
    }
}

问题引入:

Box<Integer> integerBox = new Box<>();
integerBox.setCap(900);
showBox(integerBox);
public static void showBox(Box<Integer> box) {
    Number cap = box.getCap();
    System.out.println(cap);
}

我们尝试用多态的思想新增一个Number子类的对象

又想通过重载一个showBox()方法

我们会发现这两种方法都会报错,原因是泛型Box本质来说是一个Box类的Class,因此他不能用这两种方法解决。

问题解决:

package com.yuwei.demo;

public class Test01 {
    public static void main(String[] args) {
        Box<Number> numberBox = new Box<>();
        numberBox.setCap(100);
        showBox(numberBox);

        Box<Integer> integerBox = new Box<>();
        integerBox.setCap(900);
        showBox(integerBox);

        Box<String> stringBox = new Box<>();
        stringBox.setCap("fund");
        showBox(stringBox);
    }

    public static void showBox(Box<?> box) {
        Object cap = box.getCap();
        System.out.println(cap);
    }


}

使用类型通配符?来代替具体类型的实参

类型通配符是类型实参,不是类型形参。

# 类型通配符的上限与下限

方法定义(举例):

  • 上限
ArrayList<? extends Cat>
  • 下限
ArrayList<? super Cat> 
package com.yuwei.demo1;

public class Animal {
}

package com.yuwei.demo1;

public class Cat extends Animal{
}
package com.yuwei.demo1;

public class MiniCat extends Cat{
}
package com.yuwei.demo1;

import java.util.ArrayList;

public class Test {
    public static void main(String[] args) {
        ArrayList<Animal> animals = new ArrayList<>();
        ArrayList<Cat> cats = new ArrayList<>();
        ArrayList<MiniCat> miniCats = new ArrayList<>();

        //不符合上限
        //showAnimal(animals);
        showAnimal(cats);
        showAnimal(miniCats);

        showAnimals(animals);
        showAnimals(cats);
        //不符合下限
        //showAnimals(miniCats);
    }

    /**
     * 泛型上限通配符,传递的集合类型,只能是Cat或Cat的子类类型
     * 并且方法内部也不能进行list.add()进行填充
     * 实参类型
     * @param list
     */
    public static void showAnimal(ArrayList<? extends Cat> list ){
        for (Cat cat : list) {
            System.out.println(cat);
        }
    }

    /**
     * 泛型下限通配符,传递的集合类型,只能是Cat或Cat的父类类型
     * 并且方法内部可以进行list.add()进行填充
     * 实参类型
     * @param list
     */
    public static void showAnimals(ArrayList<? super Cat> list ){
        for (Object o : list) {
            System.out.println(o);
        }
    }
}

# 类型擦除

  1. 无限制类型擦除
package com.yuwei.demo2;

public class Erasure2<T> {
    T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}
package com.yuwei.demo2;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) {
        Erasure2<Integer> integerErasure2 = new Erasure2<>();
        integerErasure2.setT(10);
        Class<? extends Erasure2> aClass = integerErasure2.getClass();
        //利用反射获取所有的成员变量
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            //打印成员变量的类型
            System.out.println(declaredField.getName()+" "+declaredField.getType().getSimpleName());
        }
        
    }
}
t Object

虽然我们这里传入的是Integer类型,但是编译结束后,T的类型并不是Integer类型,而是Object类型。这就是无限制类型擦除。

  1. 有限制类型擦除
package com.yuwei.demo2;

public class Erasure1<T extends Number> {
    T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}
Erasure1<Number> numberErasure1 = new Erasure1<>();
numberErasure1.setT(10);
Class<? extends Erasure1> aClass1 = numberErasure1.getClass();
Field[] declaredFields1 = aClass1.getDeclaredFields();
for (Field field : declaredFields1) {
    System.out.println(field.getName()+ " "+ field.getType().getSimpleName());
}
t Number

会擦除到上限

# 泛型数组

1.申明带泛型的数组

package com.yuwei.demo3;

import java.util.ArrayList;

public class ArrayTest {
    public static void main(String[] args) {

        //1.可以申明带泛型的数组引用,但是不能直接创建带泛型的数组对象
        ArrayList<String>[] strings = new ArrayList[5];
        //这样不可以
        //new ArrayList<>[5];


        ArrayList<String> strings1 = new ArrayList<>();
        strings1.add("asd");

        strings[0]=strings1;
        System.out.println(strings[0].get(0));
    }
}
asd

2.申明泛型数组

package com.yuwei.demo3;

import java.lang.reflect.Array;

public class Fruit<T> {
   //可以通过Array是newInstance方法创建T[]数组
    private T[] arrays ;

    /**
     * 初始化泛型数组
     * @param clz
     * @param len
     */
    public Fruit(Class<T> clz,int len){
        arrays = (T[]) Array.newInstance(clz, len);
    }

    /**
     * 填充元素
     * @param index
     * @param params
     */
    public void put(int index,T params){
        arrays[index]=params;
    }

    /**
     * 获取指定索引的泛型元素
     * @param index
     * @return
     */
    public T get(int index){
        return arrays[index];
    }

    public T[] getAll(){
        return arrays;
    }


}
package com.yuwei.demo3;

import java.util.Arrays;

public class ArraysTest {
    public static void main(String[] args) {
        Fruit<String> stringFruit = new Fruit<>(String.class,3);

        stringFruit.put(0,"香蕉");
        stringFruit.put(1,"苹果");
        stringFruit.put(2,"西瓜");

        System.out.println(Arrays.toString(stringFruit.getAll()));
        System.out.println(stringFruit.get(0));
    }
}
[香蕉, 苹果, 西瓜]
香蕉

# 反射的泛型

package com.yuwei.demo4;

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }


    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package com.yuwei.demo4;

import java.lang.reflect.Constructor;

public class TestReflect {
    public static void main(String[] args) throws Exception {
        //带有泛型的最后构造可以构造出具体的对象类型
        Class<Person> personClass = Person.class;
        Constructor<Person> constructor = personClass.getConstructor();
        Person person = constructor.newInstance();
		//没有的话,最后返回的是Object类型
        Class personClass1 = Person.class;
        Constructor constructor1 = personClass1.getConstructor();
        Object o = constructor1.newInstance();
    }
}
更新时间: 9/12/2022, 5:31:57 PM
А зори здесь тихие-тихие
Lube