java泛型:通过自定义ParameterizedType实现参数化类型中类型参数的替换

需求说明

如果要动态构造一个泛型参数对象(ParameterizedType),guava提供的TypeToken工具可以部分实现这个功能: 比如下面这个例子(来自guava wiki TypeToken),可以根据需要定制一个指定K,V类型的Map。

static <K, V> TypeToken<Map<K, V>> mapToken(TypeToken<K> keyToken, TypeToken<V> valueToken) {
  // where 方法是个神器,返回可以指定K,V参数类型
  return new TypeToken<Map<K, V>>() {}
    .where(new TypeParameter<K>() {}, keyToken)
    .where(new TypeParameter<V>() {}, valueToken);
}
...
TypeToken<Map<String, BigInteger>> mapToken = mapToken(
   TypeToken.of(String.class),
   TypeToken.of(BigInteger.class));
TypeToken<Map<Integer, Queue<String>>> complexToken = mapToken(
   TypeToken.of(Integer.class),
   new TypeToken<Queue<String>>() {});

但是guava提供的这个方法只能只能根据TypeParameter构造一个新的ParameterizedType,如果想根据一个已有的ParameterizedType对象替换其中的参数,上面的方法并不能实现。

比如,已经有一个Map<String, Integer>类型(也可能是HashMap,LinkedMap,Hashtable,…),现在希望将它的value type改为java.util.Date ,构造一个Map<String, Date>类型。 其实既然TypeToken工具能根据TypeParameter构造一个新的ParameterizedType,实现上面这个需求并不复杂,不知道为什么guava没有提供这个方法(我用的版本是16)。 实现这个需求最关键的就是要有一个ParameterizedType接口的实现类,有了这个实现类,你想怎么替换都成。 可惜,不论是jdk还是guava的 ParameterizedTypeImpl类都不是public的,所以没办法直接拿来用。所以就只能自己造一个。 虽然 ParameterizedType接口方法也没几个,但如何自己写个ParameterizedTypeImpl呢? 别逗了,还真打算从头自己写一个啊,再说自己的写的敢用么? 直接把jdk中的ParameterizedTypeImpl代码抄来改改就可以啦

其实这个问题我也是琢磨了好长时间才想通的。

于是我把sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl的代码几乎是原封不动的抄来,在此基础上根据需要增加了构造方法和transform方法实现了参数类型替换。完整代码如下(中文注释部分是我增加的方法),代码中用到了guava中的TypeToken工具类实现,只是为了少写些代码。

实现代码

ParameterizedTypeImpl.java

package gu.rpc.thrift;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.lang.reflect.MalformedParameterizedTypeException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;

import com.google.common.reflect.TypeToken;

/**
 * 基于jdk1.7中 {@link sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl}实现
 * @author guyadong
 *
 */
@SuppressWarnings("restriction")
class ParameterizedTypeImpl implements ParameterizedType {
    private Type[] actualTypeArguments;
    private Class<?> rawType;
    private Type ownerType;
    /**
     * 构造方法
     * 基于已有{@link ParameterizedType}实例构造一个新对象
     * @param source 不可为{@link null}
     */
    ParameterizedTypeImpl(ParameterizedType source){
        this(TypeToken.of(checkNotNull(source)).getRawType(),source.getActualTypeArguments(),source.getOwnerType());
    }
    ParameterizedTypeImpl(Class<?> rawType, Type[] actualTypeArguments, Type ownerType) {
        this.actualTypeArguments = actualTypeArguments;
        this.rawType = rawType;
        this.ownerType = ownerType != null ? ownerType : rawType.getDeclaringClass();
        this.validateConstructorArguments();
    }

    private void validateConstructorArguments() {
        TypeVariable<?>[] formals = this.rawType.getTypeParameters();
        if (formals.length != this.actualTypeArguments.length) {
            throw new MalformedParameterizedTypeException();
        }
        for (int i = 0; i < this.actualTypeArguments.length; ++i) {
        }
    }

    @Override
    public Type[] getActualTypeArguments() {
        return (Type[]) this.actualTypeArguments.clone();
    }

    @Override
    public Class<?> getRawType() {
        return this.rawType;
    }

    @Override
    public Type getOwnerType() {
        return this.ownerType;
    }

    public boolean equals(Object o) {
        if (o instanceof ParameterizedType) {
            ParameterizedType that = (ParameterizedType) o;
            if (this == that) {
                return true;
            }
            Type thatOwner = that.getOwnerType();
            Type thatRawType = that.getRawType();
            return (this.ownerType == null ? thatOwner == null : this.ownerType.equals(thatOwner))
                    && (this.rawType == null ? thatRawType == null : this.rawType.equals(thatRawType))
                    && Arrays.equals(this.actualTypeArguments, that.getActualTypeArguments());
        }
        return false;
    }

    public int hashCode() {
        return Arrays.hashCode(this.actualTypeArguments) ^ (this.ownerType == null ? 0 : this.ownerType.hashCode())
                ^ (this.rawType == null ? 0 : this.rawType.hashCode());
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        if (this.ownerType != null) {
            if (this.ownerType instanceof Class) {
                sb.append(((Class) this.ownerType).getName());
            } else {
                sb.append(this.ownerType.toString());
            }
            sb.append(".");
            if (this.ownerType instanceof ParameterizedTypeImpl) {
                sb.append(this.rawType.getName()
                        .replace(((ParameterizedTypeImpl) this.ownerType).rawType.getName() + "$", ""));
            } else {
                sb.append(this.rawType.getName());
            }
        } else {
            sb.append(this.rawType.getName());
        }
        if (this.actualTypeArguments != null && this.actualTypeArguments.length > 0) {
            sb.append("<");
            boolean first = true;
            for (Type t : this.actualTypeArguments) {
                if (!first) {
                    sb.append(", ");
                }
                if (t instanceof Class) {
                    sb.append(((Class) t).getName());
                } else {
                    sb.append(t.toString());
                }
                first = false;
            }
            sb.append(">");
        }
        return sb.toString();
    }
    /**
     * 将当前对象的类型参数中为{@code oldType}的元素替换为{@code newType}
     * @param oldType 不可为{@code null}
     * @param newType 不可为{@code null}
     * @return
     */
    public ParameterizedType transform(Type oldType,Type newType ){     
        checkNotNull(oldType);
        checkNotNull(newType);
        Type[] typeArgs = getActualTypeArguments();
        for(int i =0 ;i<typeArgs.length;++i){
            if(typeArgs[i]==oldType)
                typeArgs[i] = newType;
        }
        return new ParameterizedTypeImpl(TypeToken.of(this).getRawType(), typeArgs, getOwnerType());
    }
    /**
     * 用指定的类型参数替换当前对象的类型参数<br>
     * 新参数的个数与当前对象的类型参数个数必须一致,
     * 如果新参数数组中元素为{@code null}则对应的参数不会被替换
     * @param newTypeArguments
     * @return
     */
    public ParameterizedType transform(Type[] newTypeArguments){
        checkNotNull(newTypeArguments);
        Type[] typeArgs = getActualTypeArguments();
        checkArgument(newTypeArguments.length == typeArgs.length );
        for(int i=0;i<typeArgs.length;++i){
            if(null != newTypeArguments[i]){
                typeArgs[i] = newTypeArguments[i];
            }
        }
        return new ParameterizedTypeImpl(TypeToken.of(this).getRawType(), typeArgs, getOwnerType());
    }
}

JUnit测试用例:

TestPamameterizedImpl.java

package gu.rpc.thrift;

import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import org.junit.Test;

import com.google.common.reflect.TypeToken;

public class TestPamameterizedImpl {

    @SuppressWarnings("serial")
    @Test
    public void test() {
        TypeToken<HashMap<String, Long>> token = new TypeToken<HashMap<String,Long>>(){};
        System.out.println("oldType:"+token.getType());
        ParameterizedType newType = new ParameterizedTypeImpl((ParameterizedType) token.getType()).transform(Long.class, java.util.Date.class);
        System.out.println("newType:"+newType);
    }
}

测试输出:

oldType:java.util.HashMap<java.lang.String, java.lang.Long>
newType:java.util.HashMap<java.lang.String, java.util.Date>

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏有趣的Python

慕课网-C++远征之多态篇(下)-学习笔记

RTTI(运行时类型识别) Run-Time Type Identification typeid < - > dynamic_cast 例子: class F...

3364
来自专栏逸鹏说道

重温数据结构系列随笔:单链表(c#模拟实现)

上一节我们讲述了数据结构的基本概念,这一节让我们来讨论下单链表的概念和实现 我从书中简单摘录下单链表概念 ? 简单而言单链表的是通过许多节点构成,每个节点...

2765
来自专栏编码前线

设计模式之原型模式(Prototype)

我们知道,一个类的定义中包括属性和方法。属性用于表示对象的状态,方法用于表示对象所具有的行为。其中,属性既可以是Java中基本数据类型,也可以是引用类型。Jav...

793
来自专栏风中追风

写一个自己的springMVC

今天我们来实现一个简单的springMVC框架,可以理解为 springMVC1.0这个版本,只是功能比较简单而已;

34214
来自专栏一枝花算不算浪漫

Java中常见数据结构Map之HashMap

3457
来自专栏阿杜的世界

Java泛型之类型擦除类型擦除参考资料

学过C++模板的,在使用Java泛型的时候,会感觉到有点不疑问,例如:(1)无法定义一个泛型数组、无法调用泛型参数对象中对应的方法(当然,通过extends关键...

662
来自专栏分布式系统和大数据处理

C#中的委托和事件 - Part.1

文中代码在VS2005下通过,由于VS2003(.Net Framework 1.1)不支持隐式的委托变量,所以如果在一个接受委托类型的位置直接赋予方法名,在V...

823
来自专栏Pythonista

Golang之字符串操作(反转中英文字符串)

1002
来自专栏一个会写诗的程序员的博客

《Kotlin极简教程》第3章 Kotlin语言基础第3章 Kotlin语言基础《Kotlin极简教程》正式上架:参考资料

学习任何东西,都是一个由表及里的过程。学习一门编程语言也一样。对于一门编程语言来说,“表” 就是基本词汇(关键字、标识符等)、句子(表达式)和语法。

1112
来自专栏Java爬坑系列

【Java入门提高篇】Day3 抽象类与接口的比较

  抽象类跟接口都讲完了,现在来做一个比较。   其实说实话,没有多大的可比较性,它们是完全不同的两个东西,它们的抽象不在同一个层级上。但是为了让大家更好的理解...

1777

扫码关注云+社区