Java中的泛型 – 通配符用例

huangapple go评论67阅读模式
英文:

Generics in Java - Wildcard Usecases

问题

我们在方法参数中使用通配符(wildcard),当我们想要传递包含子类对象的列表时。但如下所示,我们可以使用类型参数来实现相同的功能。那么为什么我们需要通配符?

场景
假设我们有一个名为Department的基类,以及它的子类DevelopmentSales
Development和Sales是Department的子类型,但List<Development>和List<Sales>不是List<Department>的子类型。
因此,当我们想要将Development或Sales对象的列表作为方法参数传递时,该方法接受任何类型Department的列表,我们使用通配符。但在代码内部,我们可以看到我们可以在不使用通配符的情况下实现相同的功能。

根据Effective Java,在返回类型中使用通配符是一个非常糟糕的选择。通配符在哪些其他用例中真正有帮助呢?

class MyGenericsClass<T extends SuperClass> {

    //这两个方法的工作方式相同
    <H> void unboundedMethod1(AnotherGenericClass<H> arg) {
    }
    void unboundedMethod2(AnotherGenericClass<?> arg) {
    }

    //这两个方法的工作方式相同
    <H extends SuperClass> void boundedMethod1(AnotherGenericClass<H> arg) {
    }	
    void boundedMethod2(AnotherGenericClass<? extends SuperClass> arg) {
    }	

    //这两个方法的工作方式相同
    <U extends Building> void boundedListMethod1(List<U> list){
    }
    void boundedListMethod2(List<? extends Building> list) {
    }

    //为什么我们不能这样写?会导致编译时错误
    //<U> void notWorkingMethod1(List<U extends SuperClass> list){ //Statements }
    //<T> void notWorkingMethod2(List<U extends SuperClass> list){ //Statements }
}

在notWorkingMethods1和notWorkingMethods2中,为什么我们不能直接传递有界类型参数,而需要在返回类型之前先声明它?

英文:

We use wildcard in the method args when we want to pass list containing objects of child class. But as shown below we can achieve the same functionality using Type parameter. So why we need wildcards ?

Scenario
Lets say we have base-class named Department and its sub-classes named Development & Sales.
Development & Sales are subtypes of Department but List<Development> & List<Sales> are not subtypes of List<Department>.
So when we want to pass list of Development or Sales object as method arg which is accepting list of any type of Department we use wildcard. But inside the code we can see we can achieve the same without using wildcard.

As per Effective Java using wildcard in return type is really a bad choice. What are the other usecases where wildcard is really helpful?

class MyGenericsClass&lt;T extends SuperClass&gt; {

	//These 2 methods works the same
	&lt;H&gt; void unboundedMethod1(AnotherGenericClass&lt;H&gt; arg) {
	}
	void unboundedMethod2(AnotherGenericClass&lt;?&gt; arg) {
	}

	//These two methods works the same
	&lt;H extends SuperClass&gt; void boundedMethod1(AnotherGenericClass&lt;H&gt; arg) {
	}	
	void boundedMethod2(AnotherGenericClass&lt;? extends SuperClass&gt; arg) {
	}	

	//These two methods works the same
	&lt;U extends Building&gt; void boundedListMethod1(List&lt;U&gt; list){
	}
	void boundedListMethod2(List&lt;? extends Building&gt; list) {
	}
	
	//Why can&#39;t we write like this? Giving Compile time error
	//&lt;U&gt; void notWorkingMethod1(List&lt;U extends SuperClass&gt; list){ //Statements }
	//&lt;T&gt; void notWorkingMethod2(List&lt;U extends SuperClass&gt; list){ //Statements }
}

Inside the notWorkingMethods1 and notWorkingMethods2 why can't we pass Bounded Type parameter directly but we can do so by first declaring it before return type ?

答案1

得分: 3

First, your initial assumption that the two methods behave the same is incorrect.

Assume the generic classes were Lists. Try adding something of type H to List&lt;H&gt; and anything to List&lt;?&gt; and see if they behave the same.

Regarding the last question.

&lt;U extends Building&gt; void boundedListMethod1(List&lt;U&gt; list)

Says that U is a type that extends Building and List contains that type.

However,

&lt;U&gt; void notWorkingMethod1(List&lt;U extends Building&gt; list)

Says that there is some type U and a List that expects a type U that extends Building. Those two statements do not imply compatibility. U may not be a subClass of Building but the List expects it.

WildCards are helpful when you just want to do something without regard to type.

List&lt;Map&lt;String,Integer&gt;&gt; list = ....

for (Map&lt;?,?&gt; m : list) {
   System.out.println(m);
}

They are also useful for copying types.

public &lt;H&gt; void copy(List&lt;? extends H&gt; src, List&lt;? super H&gt; dst) {
   for (H a : src) {
       dst.add(a);
   }
}
英文:

First, your initial assumption that the two methods behave the same is incorrect.

Assume the generic classes were Lists. Try adding something of type H to List&lt;H&gt; and anything to List&lt;?&gt; and see if they behave the same.

Regarding the last question.

&lt;U extends Building&gt; void boundedListMethod1(List&lt;U&gt; list)

Says that U is a type that extends Building and List contains that type.

However,

&lt;U&gt; void notWorkingMethod1(List&lt;U extends Building&gt; list)

Says that there is some type U and a List that expects a type U that extends Building. Those two statements do not imply compatibility. U may not be a subClass of Building but the List expects it.

WildCards are helpful when you just want to do something without regard to type.

List&lt;Map&lt;String,Integer&gt;&gt; list = ....

for (Map&lt;?,?&gt; m : list) {
   System.out.println(m);
}

They are also useful for copying types.

public &lt;H&gt; void copy(List&lt;? extends H&gt; src, List&lt;? super H&gt; dst) {
   for (H a : src) {
       dst.add(a);
   }
}

</details>



huangapple
  • 本文由 发表于 2020年8月9日 20:58:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/63326612.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定