泛型地狱:我可以构建一个TypeLiteral< Set< T>>使用泛型? [英] Generics Hell: Can I construct a TypeLiteral<Set<T>> using generics?

查看:154
本文介绍了泛型地狱:我可以构建一个TypeLiteral< Set< T>>使用泛型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我能够得到下面的泛型方法的唯一方法是传递看似多余的 TypeLiteral< Set< T>> 参数。我相信应该可以通过编程的方式构造这个参数,只要给出另一个参数,但是不能弄清楚。

; T>密钥LT;设置< T>> bindMultibinder(
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 a java.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 a TypeLiteral<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 of TypeLiteral is instantiated. That is, we can't reuse the same class declaration for different values of T, but must define a new class for each T. 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&lt; Set&lt; T&gt;&gt;使用泛型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆