是否可以组合两种类型来创建另一个 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
类型。不幸的是,由于我无法控制的原因,TReq
和 TRes
没有有意义的父 类 或接口(它们是从 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]
作为此参数,其中 parameterized
是 ParameterizedType
。要制作匹配 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.bind
和 LinkedBindingBuilder.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"。
鉴于此方法签名,是否可以实现它?如果可以,怎么做?
TypeLiteral<MyClass<T, R>> combineTypes(Class<T> typeOne, Class<R> typeTwo) {
// Awesome implementation here
}
背景
我有一个接口,ApiHandler<TReq, TRes>
,我想在工厂中使用 Guice 创建,给定类型 TReq
。如有必要,我也可以传入 TRes
类型。不幸的是,由于我无法控制的原因,TReq
和 TRes
没有有意义的父 类 或接口(它们是从 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]
作为此参数,其中 parameterized
是 ParameterizedType
。要制作匹配 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.bind
和 LinkedBindingBuilder.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"。