是否可以组合两种类型来创建另一个 TypeLiteral?

Is it possible to combine two types to create another TypeLiteral?

鉴于此方法签名,是否可以实现它?如果可以,怎么做?

TypeLiteral<MyClass<T, R>> combineTypes(Class<T> typeOne, Class<R> typeTwo) {
  // Awesome implementation here
}

背景

我有一个接口,ApiHandler<TReq, TRes>,我想在工厂中使用 Guice 创建,给定类型 TReq。如有必要,我也可以传入 TRes 类型。不幸的是,由于我无法控制的原因,TReqTRes 没有有意义的父 类 或接口(它们是从 Apache Thrift 生成的)。

我以前没有使用过 Guice 或 Thrift,希望这对您有所帮助。

MyClass 的定义:public class MyClass<T, R> {}

通过添加类型参数实现方法:

public static <T, R> TypeLiteral<MyClass<T, R>> combineTypes(Class<T> typeOne, Class<R> typeTwo) {
    return new TypeLiteral<MyClass<T, R>>() {};
}
public static void main(String[] args) {
    TypeLiteral<MyClass<Integer, String>> myClassTypeLiteral = TypeTest.combineTypes(Integer.class, String.class);
}

或者简单地说:

public static <T, R> TypeLiteral<MyClass<T, R>> combineTypes() {
    return new TypeLiteral<MyClass<T, R>>() {};
}
public static void main(String[] args) {
    TypeLiteral<MyClass<Integer, String>> myClassTypeLiteral = TypeTest.<Integer, String>combineTypes();
}

TypeLiteral 有两个构造函数;正常的按预期使用 TypeLiteral<MyClass<Foo, Bar>>() {},不安全的不通用且直接创建:new TypeLiteral(type)。查看 Guice 的 TypeLiteral here 的代码,我们看到常规构造函数使用 parameterized.getActualTypeArguments()[0] 作为此参数,其中 parameterizedParameterizedType。要制作匹配 MyClass<R, T> 的东西,这将是另一个 ParameterizedType,但有两个参数,而不是一个,并且每个参数都是一个普通的 class。您现在可以创建满足所需契约的 ParameterizedType 接口的实现,构建 TypeLiteral,并将其转换为正确的 return 值(它不会具有正确的通用类型,你将不得不进行不安全的转换。

看起来像这样:

TypeLiteral<MyClass<T, R>> combineTypes(Class<T> typeOne, Class<R> typeTwo) {
    return new TypeLiteral(new ParameterizedType() {
        @RecentlyNonNull
        Type[] getActualTypeArguments() {
            return new Type[] {typeOne, typeTwo};
        }

        @RecentlyNonNull
        Type getRawType() {
            return MyClass.class;
        }

        Type getOwnerType() {
            // this is only needed for nested classes, eg. if this was Foo.Myclass this would return Foo.class.
            return null;
        }

    });
}

如果您根据运行时参数计算它,它不再是文字:它只是一个类型。

尽管您可以使用 Guava 的等效 TypeToken 框架及其实用程序,但 Guice 中内置了一个类型实用程序 class:com.google.inject.util.Types. Use newParameterizedType(Type rawType, Type... typeArguments) 来创建您想要的类型,注意 ParameterizedType 和 Class 都实现了类型。

static ParameterizedType combineTypes(Class<?> typeOne, Class<?> typeTwo) {
  return Types.newParameterizedType(MyClass.class, typeOne, typeTwo);
}

不幸的是,AbstractModule.bindLinkedBindingBuilder.to 不提供 Type 的重载;只是 Class、TypeLiteral 和 Key。幸运的是,您可以使用 Key.get(Type):

使用类型反射生成密钥
bind(Key.get(combineTypes(Foo.class, Bar.class))).to(MyClassFooBar.class);

注意,在此,ParameterizedType 本身不是参数化类型。这破坏了 Guice 的 bind EDSL 提供的一些基于泛型的巧妙保护。要使上述工作正常,您可能需要 @SuppressWarnings、return 原始类型 Key,或考虑 combineTypes return 一个 Key<MyClass<T, R>> (这需要从 Key.get(Type) 的 return 值 Key<?> 进行转换)。如果你真的必须使用 TypeLiteral,你可以通过 Key.getTypeLiteral 生成一个 TypeLiteral,但这也需要从 TypeLiteral<?> 进行转换——并且根据任何有意义的定义都不会是 "type literal"。