参数化类型: 数据的类型会影响程序的行为。
类型限定: 程序支持的数据类型是有范围的。
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