Java泛型

1. 泛型入门

泛型的英文名Generic,java5以后,引入参数化类型(parameterized type)概念,允许在创建集合时指定集合元素的类型。Java 的 参 数 化 类 型 被 称 为 泛 型(Generic)。

代码示例:

 public static void main(String[] args) {
        // 指定类型为String
        List<String> list = new ArrayList<>();
        list.add("dsf");
        // 类型不符,报错
        list.add(1);
    }

1.1 定义简单泛型类

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        
    }

    public Pair(T min, T max) {
        this.first = min;
        this.second = max;
    }

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }
}

类型变量T用<>括起来,使用大写形式。在java中,使用E表示集合中的元素,K和V表示键和值,T(U,S)表示任意类型。

使用示例:

public class ArrayAlg {
    public static Pair<String> minmax(String[] arr) {
        if (arr == null || arr.length == 0) {
            return null;
        }
        String min = arr[0];
        String max = arr[0];
        for (int i = 0, length = arr.length; i < length; i++) {
            if (min.compareTo(arr[i]) > 0) {
                min = arr[i];
            }
            if (max.compareTo(arr[i]) < 0) {
                max = arr[i];
            }
        }
        return new Pair<>(min, max);
    }

}

1.2 泛型方法

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

使用示例:

    public static void main(String[] args) {
        String sdaf = ArrayAlg.getMiddle("123", "sdaf");
    }

1.3 限定类型变量

public static <T extends Comparable> T min(T[] arr) {
        if (arr == null || arr.length == 0) {
            return null;
        }
        T min = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if (min.compareTo(arr[i]) > 0) {
                min = arr[i];
            }
        }
        return min;
    }

限定T是Comparable或Comparable的子类。

2. 擦除和转换

虚拟机没有泛型类型对象,所有对象都是普通类。

2.1 类型擦除

  • 如果类型变量没有限定,那么会用原始Object替换T。
    上文的Pair类的T没有指定上限。
public static void main(String[] args) {
        // 限定String类型
        Pair<String> pair = new Pair<String>("2134","sfsdf");
        String first = pair.getFirst();

        // pair2没有指定类型,first1只能知道是Object
        Pair pair2 = pair;
        Object first1 = pair2.getFirst();
        // 编译报错
        String first2 = pair2.getFirst();
    }

在这里插入图片描述

  • 如果类型变量有限定,那么会用上限t替换T。
public class Apple<T extends Number> {
    T size;

    public Apple(T size) {
        this.size = size;
    }

    public T getSize() {
        return size;
    }

    public void setSize(T size) {
        this.size = size;
    }
}

public static void main(String[] args) {
        Apple<Integer> apple = new Apple<>(6);
        // 这里返回Integer对象
        Integer size = apple.getSize();

        // apple2丢失尖括号的类型信息,apple2只能知道Number类型
        Apple apple2 = apple;
        Number size1 = apple2.getSize();

        // 编译报错
       Integer size2 = apple2.getSize();
    }

在这里插入图片描述

  • 步骤
    Java编译器编译泛型的步骤:
    1.检查泛型的类型 ,获得目标类型
    2.擦除类型变量,并替换为限定类型(T为无限定的类型变量,用Object替换)
    3.调用相关函数,并将结果强制转换为目标类型。
public static void main(String[] args) {
        ArrayList<String> arrayString = new ArrayList<String>();
        ArrayList<Integer> arrayInteger = new ArrayList<Integer>();
        System.out.println(arrayString.getClass() == arrayInteger.getClass());

    }

运行结果为true。getClass()总是返回原始类型。

3. 类型通配符

类型通配符: ?,代表任意类型。

3.1 通配符上限

<? extends T>, 意思是需要一个T类型或者T类型的子类。 适用于消费集合元素为主的场景。

public class Animal {
}

Cat继承Animal 。

public class Cat extends Animal {

}
  public static void handle(List<? extends Animal> list) {
        for (Animal animal : list) {
            System.out.println(animal);
        }
        // 编译报错,不能添加元素
        list.add(new Animal());
        list.add(new Cat());
    }

在这里插入图片描述

3.2 通配符下限

<? super T>,意思是需要一个T类型或者T类型的父类。适用于生产元素为主的场景。

 public static void handle2(List<? super Cat> list) {
       // 只能是Object
        for (Object cat : list) {
            System.out.println(cat);
        }
        // 编译报错
        for (Cat cat : list) {
            System.out.println(cat);
        }

        list.add(new Cat());
    }

在这里插入图片描述

3.3 例子

 public static void main(String[] args) {
        // 1.继承关系:Object > Animal > Cat > WhiteCat
        List<Animal> animals = new ArrayList<>();
        List<Cat> cats = new ArrayList<>();
        List<WhiteCat> whiteCats = new ArrayList<>();

        animals.add(new Animal());
        cats.add(new Cat());
        whiteCats.add(new WhiteCat());

        // 2.测试赋值操作
        // 下一行报错,只能赋值给Cat或Cat之类
        List<? extends Cat> extendsCatFromAnimal = animals;
        List<? super Cat> superCatFromAnimal = animals;

        List<? extends Cat> extendsCatFromCat = cats;
        List<? super Cat> superCatFromCat = cats;

        List<? extends Cat> extendsCatFromWhiteCat = whiteCats;
        // 报错,只能赋值给Cat或Cat的父类
        List<? super Cat> superCatFromWhiteCat = whiteCats;

        // 3.测试add方法
        // 均报错,无法add
        extendsCatFromCat.add(new Animal());
        extendsCatFromCat.add(new Cat());
        extendsCatFromCat.add(new WhiteCat());

        // 下一行报错,只能添加Cat或Cat的子类
        superCatFromCat.add(new Animal());
        superCatFromCat.add(new Cat());
        superCatFromCat.add(new WhiteCat());

        // 4.测试get方法
        // 所有super操作都能够返回元素,不过泛型丢失,只能是Object类型
        Object object = superCatFromAnimal.get(0);
        Object object2 = superCatFromCat.get(0);
        Object object3 = superCatFromWhiteCat.get(0);

        // 均可以返回
        Cat cat = extendsCatFromAnimal.get(0);
        Cat cat1 = extendsCatFromCat.get(0);
        // 报错,虽然WhiteCat继承Cat,但类型擦除之后是不知道的
        WhiteCat cat2 = extendsCatFromWhiteCat.get(0);

    }

在这里插入图片描述

4. java的Type

4.1 UML总览

在这里插入图片描述
Type接口有四个子接口GenericArrayType,ParameterizedType,TypeVariable,WildcardType和一个实现类Class。

4.2 相关代码

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ANNOTATION_TYPE, CONSTRUCTOR, FIELD,
        METHOD, PACKAGE, PARAMETER, TYPE, TYPE_PARAMETER, TYPE_USE})
public @interface Custom {
}



public class MyTypeTest<K, V extends @Custom Number & Serializable> {
    private Number number;
    public K k;
    public V v;
    public List<K> list = new ArrayList<>();
    public Map<String, K> map = new HashMap<>();

    public K[] tArray;
    public List<K>[] ltArray;

    public MyTypeTest testClass;
    public MyTypeTest<K, Integer> testClass2;

    public Map<? super String, ? extends Number> mapWithWildcard;

    /**
     * 泛型构造函数
     *
     */
    public <X extends Number> MyTypeTest(X x, K k) {
        this.k = k;
        number = x;
    }

    /**
     * 泛型方法
     */
    public <Y extends K> void method(Y y) {
        k = y;
    }
}

4.3 TypeVariable

4.3.1 类图关系

在这里插入图片描述

4.3.2 源码说明

/**
 * 这是各种类型变量的通用接口,类型变量是在反射方法需要时首次创建
 */
interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
    //返回此类型参数的上界列表,如果没有上界则放回Object. 例如  V extends @Custom Number & Serializable 这个类型参数,
    // 有两个上界,Number 和 Serializable 
	Type[] getBounds();
	//类型参数声明时的载体,例如 `class TypeTest<T, V extends @Custom Number & Serializable>` ,那么V 的载体就是MyTypeTest
	D getGenericDeclaration();
	// 类型变量的名称
	String getName();
	//Java 1.8加入 AnnotatedType: 如果这个这个泛型参数类型的上界用注解标记了,我们可以通过它拿到相应的注解
	AnnotatedType[] getAnnotatedBounds();
}

4.3.3 示例

public static void main(String[] args) throws Exception {
        // 获取成员变量属性v
        Field field = MyTypeTest.class.getField("v");
        
        // 获取属性的类型变量
        TypeVariable typeVariable = (TypeVariable)  field.getGenericType();

        System.out.println("类型变量的名称:" + typeVariable);
        System.out.println("类型变量的上界:" + Arrays.asList(typeVariable.getBounds()));
        System.out.println("类型变量的载体:" + typeVariable.getGenericDeclaration());
        System.out.println("类型变量的名称:" + typeVariable.getName());
        // 如果泛型参数的上界有注解标记,可以通过这个拿到注解
        AnnotatedType[] annotatedBounds = typeVariable.getAnnotatedBounds();
        System.out.println("泛型参数的上界注解1:" + Arrays.asList(annotatedBounds));
        System.out.println("泛型参数的上界注解2:" + Arrays.asList(annotatedBounds[0].getAnnotations()));

    }

执行结果。

类型变量的名称:V
类型变量的上界:[class java.lang.Number, interface java.io.Serializable]
类型变量的载体:class com.freedom.algorithm.generic.MyTypeTest
类型变量的名称:V
泛型参数的上界注解1[@com.freedom.algorithm.generic.Custom() java.lang.Number, java.io.Serializable]
泛型参数的上界注解2[@com.freedom.algorithm.generic.Custom()]

在这里插入图片描述

4.4 ParameterizedType

4.4.1 类图关系

在这里插入图片描述

4.4.2 源码说明

/**
 * ParameterizedType是指参数化类型,比如: Collection<String>.
 /
interface ParameterizedType extends Type {
     // 获取参数类型<>里面的那些值,例如Map<K,V> 那么就得到 [K,V]的一个数组
     Type[] getActualTypeArguments(); 
     // 获取参数类型<>前面的值,例如Map<K,V> 那么就得到 Map
     Type getRawType();
     // 获取其父类的类型,例如Map 有一个内部类Entry,  那么在Map.Entry<K,V> 上调用这个方法就可以获得 Map
     Type getOwnerType();
}

4.4.3 示例

  public static void main(String[] args) throws Exception {
        Field listField = MyTypeTest.class.getField("list");
        Type genericType1 = listField.getGenericType();
        System.out.println("参数化类型1:" + genericType1.getTypeName()); //参数类型1:java.util.List<T>

        Field mapField = MyTypeTest.class.getField("map");
        Type genericType2 = mapField.getGenericType();
        System.out.println("参数类型2:" + genericType2.getTypeName());//参数类型2:java.util.Map<java.lang.String, T>

        if (genericType2 instanceof ParameterizedType) {
            ParameterizedType pType = (ParameterizedType) genericType2;
            Type[] types = pType.getActualTypeArguments();
            System.out.println("参数类型列表:" + Arrays.asList(types));//参数类型列表:[class java.lang.String, T]
            System.out.println("参数原始类型:" + pType.getRawType());//参数原始类型:interface java.util.Map
            System.out.println("参数父类类型:" + pType.getOwnerType());//参数父类类型:null,因为Map没有外部类,所以为null
        }
    }

执行结果:

参数化类型1:java.util.List<K>
参数类型2:java.util.Map<java.lang.String, K>
参数类型列表:[class java.lang.String, K]
参数原始类型:interface java.util.Map
参数父类类型:null

在这里插入图片描述

4.5 GenericArrayType

4.5.1 类图关系

在这里插入图片描述

4.5.2 源码说明

/** 
 * 泛型数组类型,其基本类型是参数化类型或者类型变量,比如List<K> ,K[]。
 * GenericArrayType represents an array type whose component type is either a parameterized type or a type variable.
 /
public interface GenericArrayType extends Type {
    // 获取泛型类型数组的声明类型,即获取数组方括号 [] 前面的部分
    Type getGenericComponentType();
}

4.5.3 示例

  public static void main(String[] args) throws Exception {
        Field tArrayField = MyTypeTest.class.getField("tArray");
        GenericArrayType genericType = (GenericArrayType) tArrayField.getGenericType();
        System.out.println("数组参数类型1:" + genericType);
        TypeVariable typeVariable = (TypeVariable) genericType.getGenericComponentType();
        System.out.println("typeVariable:" + typeVariable);


        Field ltArrayField = MyTypeTest.class.getField("ltArray");
        GenericArrayType genericType2 = (GenericArrayType) ltArrayField.getGenericType();
        System.out.println("数组参数类型2:" + genericType2);
        ParameterizedType parameterizedType = (ParameterizedType) genericType2.getGenericComponentType();
        System.out.println("parameterizedType:" + parameterizedType);//数组参数类型3:T

    }

执行结果

数组参数类型1:K[]
typeVariable:K
数组参数类型2:java.util.List<K>[]
parameterizedType:java.util.List<K>

在这里插入图片描述

4.6 WildcardType

4.6.1 类图关系

在这里插入图片描述

4.6.2 源码说明

// WildcardType represents a wildcard type expression, such as ?, ? extends Number, or ? super Integer.
// 通配符类型,即带有?的泛型参数, 例如 List<?>中的?,List<? extends Number>里的? extends Number 和List<? super Integer>的? super Integer 。
public interface WildcardType extends Type {
   // 获取上界
	Type[] getUpperBounds();
	// 获取下界
	Type[] getLowerBounds();
}

4.6.3 示例

 public static void main(String[] args) throws Exception {
        Field mapWithWildcard = MyTypeTest.class.getField("mapWithWildcard");
        //先获取属性的泛型类型 Map<? super String, ? extends Number>
        Type wild = mapWithWildcard.getGenericType();
        if (wild instanceof ParameterizedType) {
            ParameterizedType pType = (ParameterizedType) wild;

            //获取<>里面的参数变量 ? super String, ? extends Number
            Type[] actualTypes = pType.getActualTypeArguments();
            System.out.println("全部类型:" + Arrays.asList(actualTypes));

            //? super java.lang.String
            WildcardType first = (WildcardType) actualTypes[0];

            //? extends java.lang.Number
            WildcardType second = (WildcardType) actualTypes[1];

            //WildcardType2: lower:[class java.lang.String]  upper:[class java.lang.Object]
            System.out.println("super 类型的: lower:" + Arrays.asList(first.getLowerBounds()) +
                    "  upper:" + Arrays.asList(first.getUpperBounds()));

            //WildcardType3: lower:[]  upper:[class java.lang.Number]
            System.out.println("extends 类型的: lower:" + Arrays.asList(second.getLowerBounds()) +
                    "  upper:" + Arrays.asList(second.getUpperBounds()));
        }
    }

执行结果

全部类型:[? super java.lang.String, ? extends java.lang.Number]
super 类型的: lower:[class java.lang.String]  upper:[class java.lang.Object]
extends 类型的: lower:[]  upper:[class java.lang.Number]

在这里插入图片描述

参考文章:秒懂Java类型(Type)系统 https://blog.csdn.net/ShuSheng0007/article/details/89520530

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值