通用方法调用
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
个实例(Rectangle
、Square
、Trapezium
)。如果你在函数 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
的数量。但在那种情况下,你还不如使用一个整数。
我一直在尝试使用 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
infoo
is. It is possible thatfoo
is aArrayList<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
个实例(Rectangle
、Square
、Trapezium
)。如果你在函数 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
的数量。但在那种情况下,你还不如使用一个整数。