泛型地狱:我可以构建一个TypeLiteral< Set< T>>使用泛型? [英] Generics Hell: Can I construct a TypeLiteral<Set<T>> using generics?
问题描述
我能够得到下面的泛型方法的唯一方法是传递看似多余的 TypeLiteral< Set< T>>
参数。我相信应该可以通过编程的方式构造这个参数,只要给出另一个参数,但是不能弄清楚。
TypeLiteral< Set< T>> superClassSet,TypeLiteral< T> superClass){
final Key< Set< T>> multibinderKey = Key.get(superClassSet,randomAnnotation);
返回multibinderKey;
}
客户端代码如下所示:
bindMultibinder(new TypeLiteral< Set< B>>(){},new TypeLiteral< A< B>(){});
其中A和B是界面。
如果我尝试以下操作(移除 TypeLiteral< Set< T> superClassSet
参数),我得到一个 java.util.Set< T> ;不能用作钥匙;它没有完全指定。
运行时错误。
受保护的< T>密钥LT;设置< T>> bindMultibinder(TypeLiteral< T> superClass){
final Key< Set< T>> multibinderKey = Key.get(
new TypeLiteral< Set< T>>(){},randomAnnotation);
返回multibinderKey;
/ em>表示所有类型参数的值是已知的。从 TypeLiteral< T>
构造完全指定的 TypeLiteral< Set< T>>
似乎是不可能的。 Guice公共API。特别是, TypeLiteral
只有两个构造函数。第一个是:
/ **
的运行时类。这只有在运行时类确定类型参数时才会产生完全指定的类型。但是,由于泛型类的所有实例共享相同的运行时类(即,
*构造一个新的类型文字。从类型
*参数派生表示类。
*
*< p>客户创建一个空的匿名子类。这样做会将类型
*参数嵌入到匿名类的类型层次结构中,因此我们可以在运行时重新构造它
*,尽管它们被擦除。
* /
@SuppressWarnings(unchecked)
protected TypeLiteral(){
this.type = getSuperclassTypeParameter(getClass());
this.rawType =(Class<?super T>)MoreTypes.getRawType(type);
this.hashCode = type.hashCode();
这个构造函数试图从<$ c $中推导出类型参数的值c> TypeLiteral
new HashSet< String>()。getClass()== new HashSet< Integer>()。getClass() code>,只有在
TypeLiteral
的非泛型子类被实例化时,才会知道类型参数。也就是说,我们不能对于不同的值T
重复使用相同的类声明,但必须为每个T
定义一个新类。麻烦,正如alf的答案所示。
这给我们留下了另一个构造函数,它更有帮助,但不是公共API的一部分:
/ **
* Unsafe。手动构造一个类型立即数
* /
@SuppressWarnings(unchecked)
TypeLiteral(Type type){
this.type = canonicalize(checkNotNull(type,type));
this.rawType =(Class< ;? super T>)MoreTypes.getRawType( this.type);
this.hashCode = this.type.hashCode();
}
package com.google.inject;
import java.util.Set;
导入com.google.inject.internal.MoreTypes;
公共类类型{
public static< T> TypeLiteral<设置< T>> setOf(TypeLiteral< T> lit){
return new TypeLiteral< Set< T>>(new MoreTypes.ParameterizedTypeImpl(null,Set.class,lit.getType()));
$ / code>测试用例:
public static void main(String [] args){
System.out.println(setOf(new TypeLiteral< String>(){}));在完美的世界里,Guice将提供一个公共的API来完成这个任务。 。The only way I was able to get the below generic method to work was to pass the seemingly redundant
TypeLiteral<Set<T>>
parameter. I believe it should be possible to construct this parameter programmatically given the other parameter, but can't figure out how.protected <T> Key<Set<T>> bindMultibinder( TypeLiteral<Set<T>> superClassSet, TypeLiteral<T> superClass) { final Key<Set<T>> multibinderKey = Key.get(superClassSet, randomAnnotation); return multibinderKey; }
Client code looks like:
bindMultibinder(new TypeLiteral<Set<A<B>>>(){}, new TypeLiteral<A<B>>(){});
Where A and B are interfaces.
If I try the following (removing the
TypeLiteral<Set<T>> superClassSet
parameter), I get ajava.util.Set<T> cannot be used as a key; It is not fully specified.
runtime error.protected <T> Key<Set<T>> bindMultibinder(TypeLiteral<T> superClass) { final Key<Set<T>> multibinderKey = Key.get( new TypeLiteral<Set<T>>() {}, randomAnnotation); return multibinderKey; }
解决方案Fully specified means that the values of all type parameters are known. Constructing a fully specified
TypeLiteral<Set<T>>
from aTypeLiteral<T>
appears to be impossible using the Guice public API. Specifically,TypeLiteral
only has two constructors. The first is:/** * Constructs a new type literal. Derives represented class from type * parameter. * * <p>Clients create an empty anonymous subclass. Doing so embeds the type * parameter in the anonymous class's type hierarchy so we can reconstitute it * at runtime despite erasure. */ @SuppressWarnings("unchecked") protected TypeLiteral() { this.type = getSuperclassTypeParameter(getClass()); this.rawType = (Class<? super T>) MoreTypes.getRawType(type); this.hashCode = type.hashCode(); }
This constructor attempts to deduce the values of type parameters from the
TypeLiteral
's runtime class. This will yield a fully specified type only if the runtime class determines the type parameter. However, because all instances of a generic class share the same runtime class (that is,new HashSet<String>().getClass() == new HashSet<Integer>().getClass()
, the type parameter is only known if a non-generic subclass ofTypeLiteral
is instantiated. That is, we can't reuse the same class declaration for different values ofT
, but must define a new class for eachT
. This is rather cumbersome, as alf's answer demonstrates.This leaves us with the other constructor, which is more helpful, but not part of the public API:
/** * Unsafe. Constructs a type literal manually. */ @SuppressWarnings("unchecked") TypeLiteral(Type type) { this.type = canonicalize(checkNotNull(type, "type")); this.rawType = (Class<? super T>) MoreTypes.getRawType(this.type); this.hashCode = this.type.hashCode(); }
We can use this constructor as follows:
package com.google.inject; import java.util.Set; import com.google.inject.internal.MoreTypes; public class Types { public static <T> TypeLiteral<Set<T>> setOf(TypeLiteral<T> lit) { return new TypeLiteral<Set<T>>(new MoreTypes.ParameterizedTypeImpl(null, Set.class, lit.getType())); } }
Testcase:
public static void main(String[] args) { System.out.println(setOf(new TypeLiteral<String>() {})); }
In a perfect world, Guice would offer a public API to accomplish this ...
这篇关于泛型地狱:我可以构建一个TypeLiteral< Set< T>>使用泛型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!