【问题标题】:Arrays of unknown type未知类型的数组
【发布时间】:2014-09-13 04:20:38
【问题描述】:

为什么表达式?[] 是非法的?我不明白为什么它比T[] 更可疑。我知道通用数组创建存在问题,所以我明白为什么不允许使用 new T[]new ?[3] 之类的东西,但我看不出 ?[] 有什么问题。例如,能够拥有像void method(?[] arr) 这样的方法签名会很好。这有什么问题?

另外,编写采用未知类型数组的方法的首选方法是什么?你应该使用

public void method(Object[] arr)

优先于

public <T> void method(T[] arr)

或者,如果类型参数只出现一次,您应该避免在方法签名中使用类型参数,这是否是常规规则的一个例外?

【问题讨论】:

  • 如果您多次使用T,则使用&lt;T&gt; 很有用,例如public &lt;T&gt; T method(T[] arr, T foo)
  • T 是在类级别或函数级别声明的类型。 ? 是一个通配符,它​​实际上从未被赋予特定值,因此它不是您可以使用的实际类型
  • 我不明白这个问题。 “?”是用于定义类型参数的通配符,而不是类型参数本身的替代品,因此您不能在通常使用类型参数的地方使用问号!
  • 也不允许有void method(? param)。同样的原因:'?不是类型。
  • 使用List,您将传递一个类型,该类型在运行时解决。使用常规数组,您是在声明一个在编译时解析的类型。你不能声明一个通配符并期望它在编译时被解析,因此你得到的错误

标签: java arrays generics


【解决方案1】:

是的,你应该使用

public void method(Object[] arr)

优先于

public <T> void method(T[] arr)

因为它们都接受相同的潜在参数集,而第一个更简单,类型参数更少。

为什么?[]这个表达式是非法的?

在语法上,因为[] 的左侧必须是实际类型。从语言上讲,因为没有必要,所以可以用Object[]代替。

一般来说,每当你想做类似(? extends X)[] 的事情时,在你的脑海中,只需将其转换为X[]。同样,每当您想将? extends X 之类的东西用作独立类型时,请考虑使用X

对于泛型类型参数,需要? extends X,因为泛型类型不是协变的(List&lt;A&gt; 不是List&lt;B&gt; 的子类型,即使AB 的子类型)。但是,数组类型是协变的(A[]B[] 的子类型,如果 AB 的子类型),因此不需要 (? extends X)[]

【讨论】:

  • 谢谢,你提出了一个很好的案例。这就是我所理解的语言点。我同意 ?[] 是不必要的,但泛型是如此复杂,我认为启用它以使它们更加一致和更容易理解是值得的。例如,如果您使用返回 T[] 的实例方法 foo 创建一个泛型类 MyClass,常识表明如果您在 MyClass> 上调用 foo,则应该得到一个 ?[]。实际上,IntelliJ 实际上将返回类型列为 ?[] (我不了解 eclipse)。不能在代码中写 ?[] 令人困惑和奇怪。
  • 此外,虽然您可以总是在想要使用Object[] 时使用?[],但如何使用并不总是那么明显。例如,如果你想去掉签名&lt;T&gt; void foo(Set&lt;T[]&gt; set)中的类型参数,你必须写void foo(Set&lt;? extends Object[]&gt; set),这真的很复杂——void foo(Set&lt;?[]&gt; set)会更清楚。
  • @pbabcdefp:这是一个单独的问题。比如&lt;T&gt; void foo(Set&lt;List&lt;T&gt;&gt; set)不能变成void foo(Set&lt;List&lt;?&gt;&gt; set);最相似的是void foo(Set&lt;? extends List&lt;?&gt;&gt; set)
  • 谢谢。我试过了,你是对的。 foo(Set&lt;List&lt;?&gt;&gt; set) 不会接受 Set&lt;List&lt;String&gt;&gt;。我可能应该放弃尝试理解泛型!签名void foo(Set&lt;List&lt;?&gt;&gt; set) 有什么意义吗?它会接受任何具体类型吗?
  • @pbabcdefp:是的,HashSet&lt;List&lt;?&gt;&gt; 是一个具体类型。它是一个集合,可以同时包含不同类型的列表。
【解决方案2】:

你混淆了类型参数和类型参数!

考虑以下泛型类型List的示例:

interface List<T> {
    void add(T element);
    int size();
    T get(int index);
}

这里,T类型参数。它是“真实”类型的占位符,在实例化泛型类型时“填充”。您可以在泛型类型的实现中使用此占位符,例如声明一个变量,或者作为另一个泛型类型的类型参数:

class LinkedList<T> implements List<T> {
    private Node<T> head; // here, T is the type argument of E in Node<E>
    ...
}

class Node<E> {
    private E element; // here, E is a placeholder for the 'real' type
    ...
}

类型参数是形参,如element 是上例中add 方法的形参。它只是在运行时指定的值的占位符!

现在让我们使用泛型类型:

List<String> list = new LinkedList<String>();

这里,String 是替换泛型类型的类型参数T类型参数

通配符 ('?') 也是某种类型参数,表示具体类型未知(并且不相关)。所以你可以在实例化泛型类型时使用它:

List<?> list = new LinkedList<String>();

同样,? 是一个类型参数,所以你不能像 类型参数那样使用它!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-25
    • 1970-01-01
    • 1970-01-01
    • 2020-11-19
    • 2016-03-13
    • 2019-04-24
    • 1970-01-01
    相关资源
    最近更新 更多