参数化类型——Java泛型

本文详细探讨了Java泛型的概念,包括参数化类型、类型限定、泛型擦除以及泛型通配符的含义。通过具体代码示例,阐述了泛型在编译期和运行期的不同表现,以及如何在实际编程中正确理解和应用泛型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

参数化类型: 数据的类型会影响程序的行为。

 

类型限定: 程序支持的数据类型是有范围的。

 

Java里的泛型: GenericType 

当我们写出下面这样的代码时,Parent<T>是一个泛型类型,它的参数范围是从顶层类型Object向下到任意一个类型。Parent<Integer>是ParameterizedType,它携带actualType信息。

package example;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.List;

/**
* Description
* <p>
* </p>
* DATE 2019/4/18.
*
* @author Kong Yuhang.
*/

public class GenericInfo {
static class Parent<T extends Number>{
List<String> stringList;
List<T> tList;
public void handle(T t){}
}
static class Child extends Parent<Integer>{}

public static void main(String[]args){

TypeVariable[] parentVars = Parent.class.getTypeParameters();
int varCount = parentVars.length;
String varName = parentVars[0].getTypeName();
String upBoundName = parentVars[0].getBounds()[0].getTypeName();
System.out.println("Parent 有一个类型参数: " + varName
+ " 上边界是 " + upBoundName);

boolean isParentParameterized = (Type)Parent.class instanceof ParameterizedType;

if( ! isParentParameterized ){
System.out.println("Parent 不是 ParameterizedType");
}

ParameterizedType parameterizedType = (ParameterizedType)Child.class.getGenericSuperclass();
String actualTypeName = parameterizedType.getActualTypeArguments()[0].getTypeName();
System.out.println(" Child 是一个 ParameterizedType"
+ "从Child的上层类型上可以获取到一个具体的类型参数 "+ actualTypeName);

try {
System.out.println(" 函数签名、参数的个数和类型确定一个方法,"
+ " 泛型的方法是使用上边界类型作为参数类型来定义的");
Method method = Child.class.getMethod("handle", Number.class);
float f = 1f;
method.invoke(new Child(), f);
System.out.println("ParameterizedType 不会影响支持的类型种类"
+ " 它只是携带了额外的信息");
}catch (Exception e){
e.printStackTrace();
}

System.out.println("所以,Java的泛型擦除是指 " +
" 不会按照actualType信息限制参数类型");

Field[] fields = Parent.class.getDeclaredFields();
for (Field field: fields){
System.out.println(field.getGenericType().getTypeName());
}

List<String> list;
System.out.println(" list 这样的局部变量是没有actualType信息的 ");

}

}

  

输出:

Parent 有一个类型参数: T 上边界是 java.lang.Number
Parent 不是 ParameterizedType
Child 是一个 ParameterizedType从Child的上层类型上可以获取到一个具体的类型参数 java.lang.Integer
函数签名、参数的个数和类型确定一个方法, 泛型的方法是使用上边界类型作为参数类型来定义的
ParameterizedType 不会影响支持的类型种类 它只是携带了额外的信息
所以,Java的泛型擦除是指 不会按照actualType信息限制参数类型
java.util.List<java.lang.String>
java.util.List<T>
list 这样的局部变量是没有actualType信息的

  

Java的的编译期间泛型检查:使用actualtype信息去检查参数类型。

Java的运行期泛型信息擦除:不使用actualtype信息去检查参数类型。

ParamterizedType 只是一种运行期间的泛型信息表示。

泛型通配符的含义:需要在编译期间做泛型检查,但是不确定actualType的具体类型。

package example;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class GenericDescription {

static class Parent{}
static class Child extends Parent{}
static class Descriptor<T extends Parent>{
public void describe(T t){}
}
public static void main (String[]args){

Child child = new Child();
Parent parent = new Parent();
Descriptor<Child> descriptor = new Descriptor<Child>();
descriptor.describe(child);
// 编译检查不允许使用Parent类型
// descriptor.describe(parent);
try{
Method method = Descriptor.class.getMethod("describe", Parent.class);
method.invoke(descriptor, parent);
System.out.println(" it's ok to describe parent type");
method.invoke(descriptor, new Object());
}catch (Exception e){
// e.printStackTrace();
String upperBoundTypeName = Descriptor.class.getTypeParameters()[0].getBounds()[0].getTypeName();
System.out.println(" it can not describe an Object type " +
"because the type variable's upper bound is " + upperBoundTypeName);
}
System.out.println("所以,Java的泛型擦除是指 " +
" 不会按照actualType信息限制参数类型");

/**
* 变量a,b满足关系a<=b时,如果函数f(x)满足f(a)<=f(b),那么f(x)是协变的
* 变量a,b满足关系a<=b时,如果函数f(x)满足f(a)>=f(b),那么f(x)是逆变的
* 变量a,b满足关系a<=b时,如果函数f(x)既不满足f(a)<=f(b),又不满足f(a)>=f(b),那么f(x)是不变的
*/

Object[] objects;
Integer[] integers = new Integer[10];
objects = integers;
System.out.println("this means 数组是协变的");

List<Object> objectList = new ArrayList<>();
List<Integer> integerList = new ArrayList<Integer>();
// objectList = integerList;
System.out.println("we can't refer objectList to integerList because 列表是不变的");
System.out.println("Java泛型的编译期检查是针对actualType的,这就可以解释下面的几种写法");

List data = new ArrayList();
// data.add(new Child());
List<? extends Child> listWithUpperBounds = data;
List<? super Child> listWithLowerBounds = new ArrayList<>();
Collections.copy(listWithLowerBounds, listWithUpperBounds);
// listWithLowerBounds.add(listWithUpperBounds.get(0));
System.out.println("we can't get an element from listWithLowerBounds " +
"because we can't refer to elements with a upper bounds type");
System.out.println("we can't put an element into listWithUpperBounds " +
"because we can't convert parent type to one of children types");

List<?> list = new ArrayList<>();
list.add(null);
listWithUpperBounds.add(null);
System.out.println("but we always can put an null into a generic list" +
"because any type's value can be null "
+ "and we can always get an object from a generic list");
// Object unknown = listWithLowerBounds.get(0);

List rawList = new ArrayList();
rawList = integerList;
Object everyThing = new Object();
rawList.add(everyThing);
System.out.println("using raw type is unsafe " +
"because we have judge the meaning of codes subjectively " +
"and sometimes it's hard to know the intentions of coders " +
"and we lost protection from the compiler with inspections");

}
/**
* 自限定和参数协变
* java 已经支持 返回值协变
* java 的参数协变可以使用泛型来实现
* 更具体的类型,更丰富的操作
*/

/**
* 这个例子里,T是当前类型的子类,且T的父类是Direction<T>
* @param <T>
*/
abstract class Direction<T extends Direction<T>>{
abstract T get();
abstract void set(T t);
}

abstract class KeepDirection<T extends KeepDirection<T>> extends Direction<T>{
@Override
T get() {
return null;
}

@Override
void set(T t) {

}
}
class WithDirection extends KeepDirection<WithDirection> {
@Override
WithDirection get() {
return super.get();
}

@Override
void set(WithDirection withDirection) {
super.set(withDirection);
}
}


class SameDirection extends Direction<SameDirection>{
@Override
SameDirection get() {
return null;
}

@Override
void set(SameDirection sameDirection) {

}
}
}

  

  

输出:

it's ok to describe parent type
it can not describe an Object type because the type variable's upper bound is example.GenericDescription$Parent
所以,Java的泛型擦除是指 不会按照actualType信息限制参数类型
this means 数组是协变的
we can't refer objectList to integerList because 列表是不变的
Java泛型的编译期检查是针对actualType的,这就可以解释下面的几种写法
we can't get an element from listWithLowerBounds because we can't refer to elements with a upper bounds type
we can't put an element into listWithUpperBounds because we can't convert parent type to one of children types
but we always can put an null into a generic listbecause any type's value can be null and we can always get an object from a generic list
using raw type is unsafe because we have judge the meaning of codes subjectively and sometimes it's hard to know the intentions of coders and we lost protection from the compiler with inspections

  

 

ParameterizedType 的用法: 表达泛型信息。 Gson反序列化。

package example;

/**
 * Description
 * <p>
 * </p>
 * DATE 2019/4/18.
 *
 * @author Kong Yuhang.
 */
class ResponseData<T> {
    private T data;
    private Integer code;
    private String errorMsg;

    public T getData() {
        return data;
    }

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

    public Integer getCode() {
        return code;
    }

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

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }
}

  

package example;

/**
 * Description
 * <p>
 * </p>
 * DATE 2019/4/18.
 *
 * @author Kong Yuhang.
 */
class Person {
    public String name;
    public Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return name + age;
    }
}

  

package example;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import com.google.gson.Gson;

/**
 * Description
 * <p>
 * </p>
 * DATE 2019/4/18.
 *
 * @author Kong Yuhang.
 */

public class UseParameterizedType {

    static class ParameterizedResponse implements ParameterizedType{
        private Type actualType;
        public ParameterizedResponse(Type actualType){
            this.actualType = actualType;
        }
        @Override
        public Type[] getActualTypeArguments() {
            return new Type[]{actualType};
        }

        @Override
        public Type getRawType() {
            return ResponseData.class;
        }

        @Override
        public Type getOwnerType() {
            return null;
        }

        public Type getTypeVar(){
            return actualType;
        }
        @Override
        public boolean equals(Object obj) {
            if(!(obj instanceof  ParameterizedResponse)){
                return false;
            }
            ParameterizedResponse another = (ParameterizedResponse)obj;
            if(!getRawType().equals(another.getRawType())){
                return false;
            }
            return getTypeVar().equals(another.getTypeVar());
        }
    }
    public static void main(String[]args){
        String data = sendRequest(null);
//        "{\"data\":{\"name\":\"xiaoming\",\"age\":20},\"code\":200}";
        Person person = dataFromResponse(data, Person.class);
        if(person!=null){
            System.out.println(" name is = " + person.getName());
            System.out.println(" age is = " + person.getAge());
        }
    }

    public static <T> T dataFromResponse( String responseJson ,Class<T> clazz){
        ResponseData responseData = new Gson().fromJson( responseJson, new ParameterizedResponse(clazz));
        System.out.println(responseData);
        if(responseData.getCode()!=200){
            System.out.println(" check code failed with error message : "+ responseData.getErrorMsg());
            return null;
        }
        return (T)responseData.getData();
    }


    public static String sendRequest(String url){
        ResponseData<Person> data = new ResponseData<>();
        Person person = new Person();
        person.setAge(20);
        person.setName("xiaoming");
        data.setData(person);
        data.setCode(200);
        return new Gson().toJson(data);
    }


}

 

输出:

example.ResponseData@5cb0d902
 name is = xiaoming
 age is = 20

  

 

 

转载于:https://www.cnblogs.com/afraidToForget/p/10727014.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值