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

时间:2018-11-06 02:16:37

标签: java generics types guice thrift

给出此方法签名,可以实现它吗?如果可以,怎么办?

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

背景

我有一个接口ApiHandler<TReq, TRes>,我正在尝试在工厂中使用给定类型TReq的Guice创建该接口。如有必要,我还可以传递类型TRes。不幸的是,TReqTRes没有有意义的父类或接口,原因超出了我的控制范围(它们是从Apache Thrift生成的)。

3 个答案:

答案 0 :(得分:3)

如果您基于运行时参数进行计算,则不再是 literal :它只是类型。

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

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

不幸的是,AbstractModule.bindLinkedBindingBuilder.to没有为Type提供重载;只是Class,TypeLiteral和Key。幸运的是,您可以使用Key.get(Type)使用Type反射地生成Key:

bind(Key.get(combineTypes(Foo.class, Bar.class))).to(MyClassFooBar.class);

请注意,在此,ParameterizedType本身不是参数化类型。这使Guice的bind EDSL提供了一些巧妙的基于泛型的保护。要使以上功能正常工作,您可能需要@SuppressWarnings,返回原始类型Key或考虑让combineTypes返回Key<MyClass<T, R>>(这需要从{进行强制转换{1}}的返回值Key.get(Type))。如果确实必须使用TypeLiteral,则可以通过Key.getTypeLiteral生成TypeLiteral,但这也需要从Key<?>进行强制类型转换-并且在任何有意义的定义下都不会是“类型文字”。

答案 1 :(得分:1)

TypeLiteral有两个构造函数。正常使用的TypeLiteral<MyClass<Foo, Bar>>() {},不安全的不是通用的,而是直接创建的:new TypeLiteral(type)。查看Guice的TypeLiteral here的代码,我们看到常规构造函数对此参数使用parameterized.getActualTypeArguments()[0],其中parameterizedParameterizedType。为了制作与MyClass<R, T>相匹配的内容,这将是另一个ParameterizedType,但具有两个参数(而不是一个),每个参数将是一个普通类。现在,您可以创建一个ParameterizedType接口的实现,以实现所需的合同,构造一个TypeLiteral并将其强制转换为正确的返回值(它将没有正确的泛型类型,您将必须进行不安全的投射)。

它看起来像这样:

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;
        }

    });
}

答案 2 :(得分:-1)

我以前没有使用过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();
}