java泛型一点理解

2021/09/20 Java 共 3497 字,约 10 分钟

用途

实现多态。泛型在实际的应用中,提高了代码抽象程度,把复杂而又相似的逻辑统一收束,无论在减少代码量的方面,还是在后续的维护与重构的方面,都能起到极大的作用。

这是很抽象而且假大空的描述了,把它具体化,变成例子。简单点的,List里面可以放String,但你放了String就不应该放Number,泛型的机制保证了这些部分。复杂点的,一个事件响应器handler抽象类,应该有不同的具体的类的实现,运用泛型能保证这些不同的具体的实现有相似的行为,都能达到事件处理的目的,但达成的方式有所不同。

实现机制

泛型擦除和强制类型转换。

java的泛型是被实现的特性,编译生成的字节码文件中是不含泛型中的类型信息的,jvm在运行的时候是没有泛型的,泛型只存在于源码中,在编译后就会消失,这个过程就是泛型的擦除。

大部分泛型的奇妙特性,都可以从泛型擦除这个底层的机制来找原因。

泛型类型

结构

它们和class都是Type的子接口,共同属于java.lang.reflect包,自jdk1.5起加入。

结构

同时,Class<T>类也是Type的子类。

例子

    private class TypeTest<T> {

        private T[] ts;

        public void wildCardTest(List<? extends T> list) {

        }

        public TypeTest(T[] tss) {
            this.ts = tss;
        }

    }

在这个例子中,一个实例化的TypeTest<T> ,比如TypeTest<String>,就是一个ParameterizedType

TypeTest<T>中的T,就是TypeVariable

List<? extends T> list中的? extends T 就是WildcardType

T[] ts为GenericArrayType

他们有很多实用的api,可以在反射的时候用,jdk里注释写的很清楚,这里稍稍看一看。


package java.lang.reflect;


public interface GenericArrayType extends Type {

	// 返回泛型数组中的泛型类型
    Type getGenericComponentType();
}


package java.lang.reflect;


public interface ParameterizedType extends Type {
	
	// 返回 ParameterizedType的 TypeVariable,如
	// List<ArrayList> a1;//返回 ArrayList,Class类型
	// List<ArrayList<String>> a2;返回 ArrayList<String>, ParameterizedType类型
    Type[] getActualTypeArguments();

	// 返回 rawType
	// List<ArrayList> a1;//返回List
    Type getRawType();

	// 内部类返回外部的 rawType
    Type getOwnerType();
}


package java.lang.reflect;


public interface TypeVariable<D extends GenericDeclaration> 
extends Type,AnnotatedElement {

	// 返回表示此 TypeVariable上限的Type对象数组。 
	// 如果没有明确声明上限,则上限为Object。
    Type[] getBounds();

	// 返回表示为此 TypeVariable声明的 GenericDeclaration 对象。 
    D getGenericDeclaration();

	// 返回 TypeVariable在源码中的名字,如T,K,V
    String getName();

	// 返回一个 AnnotatedType 对象数组
	// 这些对象表示使用类型来表示由此TypeVariable 表示的类型参数的上限。 
    // 数组中对象的顺序对应于类型参数声明中边界的顺序。
    AnnotatedType[] getAnnotatedBounds();
}

package java.lang.reflect;


public interface WildcardType extends Type {

	// 通配符上界
    Type[] getUpperBounds();

	// 通配符下界
    Type[] getLowerBounds();
    // one or many? Up to language spec; currently only one, but this API
    // allows for generalization.
}

遇到的问题

泛型机制会有一些机制,在这里稍微做点总结。

public class Fruit{
…………
}

public class Melon extends Fruit{
…………
}

List<Fruit> fruits=new ArrayList<>();
List<Melon> melons=new ArrayList<>();
if(fruits.getClass()=melon.getClass()){}	// true

if (fruits instanceof List){}	// true
if (fruits instanceof List<String>){}	// 无法通过编译
List<Fruit>[] qwe=new List[2];	// 合法
List<Fruit>[] table=(List<Fruit>[]) new List<?>[10];	// 合法
List<Fruit>[] qwe=new List<Fruit>[2];	// 无法通过编译

运行时无法保留泛型的声明,这是因为被擦除了类型,只能保留原始类型,这个特性在反射的实际运用中,会带来很多麻烦。

    private class TypeTest<T> {
        public TypeTest(T[] tss) {
            T t= new T();	// 无法通过编译
            T t= T.class.newInstance();	// 无法通过编译
        }
    }

不能实例化TypeVariable。

    public static  <T> Melon<T> getMelon(Class<T> clazz){
        try {
            return new Melon<>(clazz.newInstance(),clazz.newInstance());
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

Melon melon=Melon.getMelon(Melon.class);

需要通过反射的方式,间接的实例化对象。

通配符类型

Java中泛型是不变的,而数组是协变的.

不变,协变,逆变的定义

逆变与协变用来描述类型转换(type transformation)后的继承关系

数组是协变的,导致数组能够继承子元素的类型关系 :Number[] arr = new Integer[2]; -> OK

泛型是不变的,即使它的类型参数存在继承关系,但是整个泛型之间没有继承关系 :ArrayList<Number> list = new ArrayList<Integer>(); -> Error

带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取

下面的这个经典的复制可以配合理解这个问题

    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        ...
        if (srcSize < COPY_THRESHOLD ||
            (src instanceof RandomAccess && dest instanceof RandomAccess)) {
            for (int i=0; i<srcSize; i++)
                // dest 接收数据, src 提供数据
                dest.set(i, src.get(i));
        }
        ...
    }

reference

Java中与泛型相关的接口 之 术语定义

java泛型 通配符详解及实践

我眼中的Java-Type体系(1)

文档信息

Search

    Table of Contents