Java 何时使用通用方法,何时使用通配符?
我正在读有关通用方法的文章。我对何时使用通配符和何时使用泛型方法的比较感到非常困惑。 从文件中引用Java 何时使用通用方法,何时使用通配符?,java,generics,wildcard,Java,Generics,Wildcard,我正在读有关通用方法的文章。我对何时使用通配符和何时使用泛型方法的比较感到非常困惑。 从文件中引用 接口集合{ 公共图书馆(c组); 公共布尔addAll(Collection通配符方法也是泛型的-您可以使用某些类型范围调用它 语法定义了一个类型变量名。如果一个类型变量有任何用途(例如,在方法实现中或作为其他类型的约束),那么将其命名是有意义的,否则您可以使用?作为匿名变量。因此,这看起来只是一条捷径 此外,在声明字段时,?语法是无法避免的: class NumberContainer { S
接口集合{
公共图书馆(c组);
公共布尔addAll(Collection通配符方法也是泛型的-您可以使用某些类型范围调用它
语法定义了一个类型变量名。如果一个类型变量有任何用途(例如,在方法实现中或作为其他类型的约束),那么将其命名是有意义的,否则您可以使用?
作为匿名变量。因此,这看起来只是一条捷径
此外,在声明字段时,?
语法是无法避免的:
class NumberContainer
{
Set<? extends Number> numbers;
}
类号容器
{
Set在某些地方,通配符和类型参数的作用是相同的。但也有某些地方,您必须使用类型参数
如果要对不同类型的方法参数强制执行某种关系,则不能使用通配符,必须使用类型参数
以您的方法为例,假设您希望确保传递给copy()
方法的src
和dest
列表应为相同的参数化类型,您可以使用如下类型参数执行此操作:
public static <T extends Number> void copy(List<T> dest, List<T> src)
在第二种情况下,您可以将List
和List
作为dest
和src
传递。因此,将元素从src
移动到dest
将不再是类型安全的。
如果您不需要这种关系,那么您可以完全不使用类型参数
使用通配符和类型参数之间的其他一些区别是:
- 若您只有一个参数化类型参数,那个么您可以使用通配符,尽管类型参数也可以工作
- 类型参数支持多个边界,而通配符不支持
- 通配符支持上界和下界,类型参数只支持上界。因此,如果要定义一个方法,该方法采用类型为
Integer
的List
,或者它是超类,则可以执行以下操作:
public void print(List<? super Integer> list) // OK
public void print(List在您的第一个问题中:这意味着如果参数的类型和方法的返回类型之间存在关系,则使用泛型
例如:
public <T> T giveMeMaximum(Collection<T> items);
public <T> Collection<T> applyFilter(Collection<T> items);
在这两个示例中,无论集合中项目的类型如何,返回类型都将是int
和boolean
在您的示例中:
interface Collection<E> {
public boolean containsAll(Collection<?> c);
public boolean addAll(Collection<? extends E> c);
}
及
你可以
List<? extends Fruit> basket = new ArrayList<? extends Fruit>();
basket.add(new Apple());
basket.add(new Pear());
basket.add(new Tomato());
List<Fruit> fridge = new ArrayList<Fruit>();
Collections.copy(fridge, basket);// works
List我会试着逐一回答你的问题
我们不认为通配符类似于(Collection考虑下面James Gosling第四版Java编程中的示例,其中我们希望合并2个SingleyLinkQueue:
public static <T1, T2 extends T1> void merge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){
// merge s element into d
}
public static <T> void merge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){
// merge s element into d
}
公共静态无效合并(SinglyLinkQueue d,SinglyLinkQueue s){
//将s元素合并到d元素中
}
公共静态无效合并(SinglyLinkQueue d,SinglyLinkQueue此处未列出的另一个差异
static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o); // correct
}
}
来自ArrayToCollection的静态void(T[]a,集合c){
对于(TO:a){
c、 添加(o);//正确
}
}
但以下操作将导致编译时错误
static <T> void fromArrayToCollection(T[] a, Collection<?> c) {
for (T o : a) {
c.add(o); // compile time error
}
}
来自ArrayToCollection的静态void(T[]a,集合c){
对于(TO:a){
c、 add(o);//编译时错误
}
}
据我所知,当严格需要通配符时,只有一个用例(即,可以表示使用显式类型参数无法表示的内容)。这就是需要指定下限的时候
除此之外,通配符还可以编写更简洁的代码,如您提到的文档中的以下语句所述:
泛型方法允许使用类型参数来表示
方法的一个或多个参数类型之间的依赖关系
和/或其返回类型
方法不应使用
[……]
使用通配符比声明显式更清晰、更简洁
类型参数,因此应尽可能首选
[……]
通配符还有一个优点,即它们可以在外部使用
方法签名,如字段、局部变量和数组的类型
主要->通配符在非泛型方法的参数/参数级别强制泛型。
注意。默认情况下,它也可以在genericMethod中执行,但在这里,我们可以使用T本身而不是?来执行
包装仿制药
public class DemoWildCard {
public static void main(String[] args) {
DemoWildCard obj = new DemoWildCard();
obj.display(new Person<Integer>());
obj.display(new Person<String>());
}
void display(Person<?> person) {
//allows person of Integer,String or anything
//This cannnot be done if we use T, because in that case we have to make this method itself generic
System.out.println(person);
}
}
class Person<T>{
}
公共类DemoWildCard{
公共静态void main(字符串[]args){
DemoWildCard obj=新的DemoWildCard();
对象显示(新人物());
对象显示(新人物());
}
无效显示(人){
//允许使用整数、字符串或任何字符
//如果我们使用T,这是不可能做到的,因为在这种情况下,我们必须使这个方法本身具有通用性
系统输出打印项次(人);
}
}
班主任{
}
所以通配符有这样的特定用例。?表示未知
一般规则适用于:
您可以从中读取,但不能写入
给定简单的pojo汽车
class Car {
void display(){
}
}
这将编译
private static <T extends Car> void addExtractedAgain1(List<T> cars) {
T t = cars.get(1);
t.display();
cars.add(t);
}
private static void addExtractedAgain1(列出车辆){
T=cars.get(1);
t、 显示();
加上(t);
}
此方法无法编译
private static void addExtractedAgain2(List<? extends Car> cars) {
Car car = cars.get(1);
car.display();
cars.add(car); // will not compile
}
private static void addExtractedAgain2(列表这不应该是评论吗?@BuhakeSindi抱歉,什么不清楚?为什么-1?我认为它回答了这个问题。这是一个奇怪的答案。它根本无法解释为什么需要使用?
。您可以将其重写为“public static void copy(列表dest,列表src))在这种情况下,很明显发生了什么。@kan。这才是真正的问题。您可以使用类型参数强制执行相同的类型,但不能使用通配符。使用两个不同的
interface Fruit{}
class Apple implements Fruit{}
class Pear implements Fruit{}
class Tomato implements Fruit{}
List<? extends Fruit> basket = new ArrayList<? extends Fruit>();
basket.add(new Apple());
basket.add(new Pear());
basket.add(new Tomato());
List<Fruit> fridge = new ArrayList<Fruit>();
Collections.copy(fridge, basket);// works
class Collections {
public static <T, S extends T> void copy(List<T> dest, List<S> src) {
...
}
List<Apple> basket = new ArrayList<Apple>();
basket.add(new Apple());
basket.add(new Apple());
basket.add(new Apple());
List<Fruit> fridge = new ArrayList<Fruit>();
Collections.copy(fridge, basket); /* works since the basket is defined as a List of apples and not a list of some fruits. */
Collections.copy(List<Number> dest, List<? extends Number> src);
//For double
Collections.copy(List<Number> dest, List<Double> src); //Double extends Number.
//For int
Collections.copy(List<Number> dest, List<Integer> src); //Integer extends Number.
public static <T1, T2 extends T1> void merge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){
// merge s element into d
}
public static <T> void merge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){
// merge s element into d
}
static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o); // correct
}
}
static <T> void fromArrayToCollection(T[] a, Collection<?> c) {
for (T o : a) {
c.add(o); // compile time error
}
}
public class DemoWildCard {
public static void main(String[] args) {
DemoWildCard obj = new DemoWildCard();
obj.display(new Person<Integer>());
obj.display(new Person<String>());
}
void display(Person<?> person) {
//allows person of Integer,String or anything
//This cannnot be done if we use T, because in that case we have to make this method itself generic
System.out.println(person);
}
}
class Person<T>{
}
class Car {
void display(){
}
}
private static <T extends Car> void addExtractedAgain1(List<T> cars) {
T t = cars.get(1);
t.display();
cars.add(t);
}
private static void addExtractedAgain2(List<? extends Car> cars) {
Car car = cars.get(1);
car.display();
cars.add(car); // will not compile
}
List<?> hi = Arrays.asList("Hi", new Exception(), 0);
hi.forEach(o -> {
o.toString() // it's ok to call Object methods and methods that don't need the contained type
});
hi.add(...) // nothing can be add here won't compile, we need to tell compiler what the data type is but we do not know