通用方法调用

Generic Method Calls

我一直在尝试使用 Generic,很快我就 运行 遇到了我无法解释的事情。
例如:

import java.util.*;
public class Main {
    public static void main(String[] args) {
        //We cannot create List<?> l = new ArrayList<?>();
        List<?> l = MagicClass.factory();
    }
    static class MagicClass {
        public static <T> List<T> factory() {
            return new ArrayList<T>();
        }
    }
}

我不明白 <T> 怎么可以是显式类型参数,而这个

<?>

未定义。

<?> 是一个 wildcard,它的意思等于:"any type you like"。现在由于 <?> 不对使用的类型做任何假设,T 只能更 专业化 。因此 Java 将 Foo<T> 视为 Foo<?>。另一方面,这样做是相当不明智的,因为Java对代码进行类型检查的手段较少。因此,Java 通常会拒绝调用任何将类型参数用作输入的方法。

Example: the following code won't compile:

    ArrayList<?> foo = new ArrayList<String>();
    foo.add("bar");

This because, Java doesn't know what the T in foo is. It is possible that foo is a ArrayList<Integer>, thus to be safe, it is not allowed.

如果您想要创建一个可以接受所有类型 List<?> 实例的方法,而不考虑其中存储的对象,则可以使用它。

你永远不能用通配符构造实例

以下代码无法编译:

ArrayList<?> foo = new ArrayList<?>();

这是因为在构造的时候,Java必须知道你会选择哪种类型。


但在很多情况下它被用作有界通配符。以典型为例:

public void drawAll(List<? extends Shape> shapes) {
    //...
}

此方法给出了 Shape 的列表。现在,如果您将其写为 List<Shape>,则需要您提供 ArrayList<Shape>LinkedList<Shape> 或其他集合。但也许您已 专门化 您的集合以仅包含 Quadrilateral 个实例(RectangleSquareTrapezium)。如果你在函数 drawAll(List<Shape> shapes) 上使用 ArrayList<Quadrilateral>,你会得到一个类型错误:实际上,List<Quadrilateral> 不是 List<Shape>,但是使用边界,你强制执行通用类型 extends 例如来自另一个类型。

使用通配符实例化泛型会产生编译错误,因为通配符意味着 任何类型,而实例化的 ArrayList 应该具有具体类型。通配符可用于变量或参数类型,但不能用于创建泛型类型的实例。

this tutorial page中也提到了这一点:

In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific). The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype.

另一方面,在factory方法中,T是编译器会推断的类型参数,由于List<?>表示任何类型,所以它是被编译器接受。

We cannot create List l = new ArrayList();

如果你真的想做,你可以做

List<?> l = new ArrayList<Object>();

List<?> l = new ArrayList<String>();

List<?> l = new ArrayList<Math>();

List<?> l = new ArrayList<SomeBogusIrrelevantClass>();

全部正确。只需选择满足边界的任何类型;它甚至不必与您正在做的事情相关。

由此,您可以明白为什么您尝试做的事情是荒谬的——您有一个可能是 ArrayList<Math> 或可能是 ArrayList<SomeBogusIrrelevantClass> 的列表(您不知道) .那么你能用它做什么呢?不多。

您不能向其中添加任何内容(null 除外)。所以你有一个列表,其中的元素只能有值 null;唯一可以变化的是 null 的数量。但在那种情况下,你还不如使用一个整数。