Java中的双通用实例化

时间:2015-05-12 12:58:41

标签: java generics

我有一个使用泛型的类。因为它需要对泛型类型的引用,所以构造函数具有Class参数(如this question中所述),所以它看起来像这样:

MyGenericClass<T>
public MyGenericClass( final Class<T> valueClass ) {
    this.clazz = valueClass;
}

现在我想将MyGenericClass作为T传递,但我无法使其正常工作。

MyGenericClass<MyGenericClass<String>> foo = 
        new MyGenericClass<MyGenericClass<String>>(MyGenericClass<String>.class);

给我一​​个编译错误。

看起来它是类型擦除的问题(见this SO question)。有了这个双层泛型,我不确定它是否可以做我想做的事。

有什么建议吗?

UTA:

我无法发布整个代码,因为它是公司代码,而且我不确定发布的规则。该类本质上是一个MultipleValueMap,它将单个键映射到值列表。类对象用于在列表中创建值的类型数组(使用list.toArray(T[])

在这种情况下,我希望有一个映射到多个值列表的键。这导致了这个问题。

5 个答案:

答案 0 :(得分:3)

泛型在这种情况下开始崩溃。 List<String>类型,但不是,因此只有List.class

Guava的TypeToken是为这些情况而创建的。

class MyGenericClass<T> {
    private Class<T> raw;

    /* the code in this constructor
     * has similar semantics to e.g.
     *  MyGenericClass<String>[] arr = new MyGenericClass[10];
     * when passing a
     *  new TypeToken<MyGenericClass<String>>() {}
     */
    MyGenericClass(TypeToken<T> type) {
        @SuppressWarnings("unchecked")
        final Class<T> unchecked = (Class<T>) type.getRawType();
        raw = unchecked;

        @SuppressWarnings("unchecked")
        T[] arr = (T[]) Array.newInstance(type, 10);
        //
    }
}
MyGenericClass<MyGenericClass<String>> g =
    new MyGenericClass<>(new TypeToken<MyGenericClass<String>>() {});

否则你的选择有点受限。有一些kludgy选项:

MyGenericClass<MyGenericClass<String>> g =
    new MyGenericClass<MyGenericClass<String>>(
        (Class) MyGenericClass.class);

也可能将Class<T>更改为Class<? extends T>并传递一个子类(我看到有人已经发布,所以我很难重复它)。

如果您不需要将阵列返回外部世界,您可以这样做:

class MyGenericClass<T> {
    private Class<? super T> maybeSuper;

    MyGenericClass(Class<? super T> maybeSuper) {
        this.maybeSuper = maybeSuper;

        // array which accepts T
        Object[] arr = (Object[]) Array.newInstance(maybeSuper, 10);
    }
}

然后你可以这样做:

MyGenericClass<MyGenericClass<String>> g =
    new MyGenericClass<MyGenericClass<String>>(
        MyGenericClass.class);

因为原始类型是参数化类型的超类型。

答案 1 :(得分:0)

我认为你不想使用Class。这需要Class个对象。

只需使用T或对象&lt; T&gt;相反,如果类。这就是你想要的例子

答案 2 :(得分:0)

从我看到的情况来看,在我看来,你想以Class<MyGenericClass<String>>之类的方式实例化MyGenericClass<String>.class。这种方式是被禁止的,你必须使用另一个选项:你可以接受Class<? extends T>作为构造函数的参数,并创建类似内部类的东西,它通过具体的泛化扩展泛型类:

private static class MGCString extends MyGenericClass<String> {
}

并将您的新成员实例化为

MyGenericClass<MyGenericClass<String>> foo = new MyGenericClass<>(MGCString.class);

来自@Radiodef的注意事项:这取决于您对所创建的阵列的处理方式。如果你想放置,例如a new GenericClass<String>(String.class)进入数组,然后这不起作用。 (它抛出了数组存储异常。)如果你也用反射实例化元素(例如通过class.newInstance())那么它可能会起作用。

更新后

由于您已发布问题的更新,我现在可以回答您的问题:)将List转换为数组的常用方法是将List的元素移动到某个数组,由用户提供。

例如,我们可以查看List.toArray(T[])。这个方法将所有列表的元素放入传递的数组中,以防它足够大。否则它会创建新数组(是的,我们可以通过运行a.getClass()获得它的类型。)

此外,在Java 8中,Streams API引入了the brand new way来执行相同的操作:为T[]提供生成器:

//get new Stream<String>, for example, someway
        .toArray(String[]::new)

如果您创建了一些接受IntFunction<T[]> arrayGenerator并运行arrayGenerator.apply(list.size())以获取所请求大小数组的方法,则可以将此功能添加到代码中。

答案 3 :(得分:0)

You can do it, sort of..,

Your basic problem is that the type T of class ViewController: UIViewController { @IBOutlet weak var myGradientView: GradientView! override func viewDidLoad() { super.viewDidLoad() } override func viewDidLayoutSubviews() { self.myGradientView.gradientWithColors(UIColor.whiteColor(), UIColor.blueColor()) } } can not itself be generic, that is:

Class<T>

But! You can get around this by creating a type to prepresent the typed type, eg:

Class<List> a; // OK
Class<List<String>> b; // can't do this

Translating this to your situation, you would have to create an interface to prepresent MyGenericClass (to avoid the constructor problem in the abstract type) then have subtypes of that and pass those in to your constructors.

答案 4 :(得分:0)

这取决于你的目的是什么。

您可以通过对Class<MyGenericClass<String>>执行某些不安全的转换来获取MyGenericClass.class类型的表达式:

(Class<MyGenericClass<String>>)(Class<?>)MyGenericClass.class

但这真的是你想要的吗?为什么需要不安全的演员?

从某种意义上说,你不能真正地&#34;有一个Class<MyGenericClass<String>>,因为Class<T>可以在运行时检查给定对象是否为T类型,并使用其.isIntance()方法,但是您永远无法检查对象&#39}运行时的泛型类型参数,因为它已被删除。然而,出于某些目的,它可能已经足够好了#34;假装&#34; MyGenericClass.class(类型Class<MyGenericClass>)是Class<MyGenericClass<String>>(上面的不安全演员正在做的事情)。

因此,它归结为您的函数对类对象的作用。根据您的描述,您说&#34;类对象用于在列表中创建值的类型数组(使用list.toArray(T[]))&#34;。如您所知,Java中不允许创建通用数组。因此,即使您直接使用具体类而不是T,您仍然无法new MyGenericClass<String>[42]。您可以做的最好的事情是(MyGenericClass<String>[])new MyGenericClass[42](MyGenericClass<String>[])new MyGenericClass<?>[42]。换句话说,你是使用raw-type类创建数组,然后&#34; pretending&#34;创建的数组是使用不安全的强制转换的参数化类型的数组。

这类似于上面使用类对象的转换:

(Class<MyGenericClass<String>>)(Class<?>)MyGenericClass.class

- 您正在使用原始类型对象,然后&#34;假装&#34;该类是使用不安全的强制转换的参数化类型,因此当您创建数组时,您会认为它是参数化类型的数组。