【问题标题】:Varargs of type Class in JavaJava 中 Class 类型的可变参数
【发布时间】:2014-11-28 00:03:50
【问题描述】:

例如,如果我有一个方法使用可变参数作为类类型,该类类型扩展了一个超类,例如 这个:

public static <E extends Example> void test(Class<E>... es){}

然后我尝试使用 Example 的两个不同子类调用该方法,只有在创建一个包含两个类的数组时才能这样做。

//this does not work
test(E1.class,E2.class);
//this does work
test(new Class[]{E1.class,E2.class});
public class E1 extends Example {}
public class E2 extends Example {}

这是为什么?

【问题讨论】:

    标签: java generics superclass variadic-functions


    【解决方案1】:

    此行无法编译:

    test(E1.class,E2.class);
    

    只有一个类型参数E,Java 必须与参数的推断类型完全匹配。它无法推断出Example,因为对象是Class&lt;E1&gt;Class&lt;E2&gt;,而不是Class&lt;Example&gt;。 Java 泛型的不变性阻止了这种情况的发生。

    您可以通过在test 的泛型类型参数上引入上限通配符来解决此问题:

    public static <E extends Example> void test(Class<? extends E>... es)
    

    这允许 Java 通过使用 E1E2 满足上限通配符来推断 ExampleE

    第二行创建Classes 的原始数组,绕过泛型并生成“未经检查的调用”警告。

    new Class[]{E1.class,E2.class}
    

    如果您尝试在此处为Class 提供类型参数,则使用任何中间合理的类型参数都会出现编译器错误:

    // Needs Class<Example> but found Class<E1> and Class<E2>
    test(new Class<Example>[]{E1.class,E2.class});
    
    // Needs Class<E1> but found Class<E2>
    test(new Class<E1>[]{E1.class,E2.class}); 
    
    // Needs Class<E2> but found Class<E1>
    test(new Class<E2>[]{E1.class,E2.class}); 
    

    通过在此处使用通配符来满足推理只是在这里发现了真正的问题——创建泛型数组。

    // Generic array creation
    test(new Class<? extends Example>[]{E1.class,E2.class});
    

    【讨论】:

    • 非常感谢。它从来没有对我的程序有害,但我肯定已经有一段时间摸不着头脑了。我非常感谢您的澄清。
    【解决方案2】:

    您定义扩展 Example 的单个类的通用 E。您不能在调用中引用两个不同的类,因为它不知道 E 是什么类型。它只需要一种类型。

    虽然这不起作用:

    test(E1.class, E2.class);
    

    这样做:

    test(E1.class, E1.class);
    

    您可以使用数组执行此操作的原因是类型擦除。编译器没有看到数组中的类是不同的。

    如果您更改方法以接受任何扩展 Example 的类,它将起作用。

    public static void test(Class<? extends Example>...classes)
    

    【讨论】:

    • 我认为你已经解释得很好了。但是要与 OP 的示例相同,您的签名应该是 public static &lt;E extends Example&gt; void test(Class&lt;? extends E&gt;... classes)。不过,它会(在任何情况下)发出-Xlint:unchecked 警告。
    • @5gon12eder:您给出的签名与此答案中的签名完全相同,即它们接受完全相同的参数类别。应该始终首选更简单的那个(在这个答案中给出的那个),因为类型参数是不必要的。
    • @newacct 它们接受相同类别的参数,但如果您想引用泛型类型,则需要更详细的语法。因为在 OP 中,test 返回 void 并且有一个空的主体,你可以说我们不想引用类型,但一般来说,冗长的语法也更强大。
    • @5gon12eder:从外部代码的角度来看,它并没有“更强大”。即使在极不可能的情况下代码需要引用E,您的方法也可以简单地将参数传递给通用的私有方法。
    • @5gon12eder 由于该方法无效,因此无需明确声明 E。
    猜你喜欢
    • 2023-01-09
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 2022-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多