javac 无法编译上限通配符,但 Eclipse 可以
javac cannot compile upper bounded wildcard but Eclipse can
我使用的代码库一直依赖 Eclipse 进行编译,直到现在。我的objective是用javac
(通过ant
)编译它来简化构建过程。该项目在 Eclipse(版本 2019-12(4.14.0))中编译无投诉,但 javac
(OpenJDK,版本 1.8.0_275 和 14.0.2)产生 method ... cannot be applied to given types
错误涉及上限通配符。
重现步骤
请注意,在撰写本文时,存储库为 64 MB:
git clone git@github.com:jamesdamillington/CRAFTY_Brazil.git
cd CRAFTY_Brazil && git checkout 550e88e
javac -Xdiags:verbose \
-classpath bin:lib/jts-1.13.jar:lib/MORe.jar:lib/ParMa.jar:lib/ModellingUtilities.jar:lib/log4j-1.2.17.jar:lib/repast.simphony.bin_and_src.jar \
src/org/volante/abm/agent/DefaultSocialLandUseAgent.java
错误信息输出:
src/org/volante/abm/agent/DefaultSocialLandUseAgent.java:166: error: method removeNode in interface MoreNetworkModifier<AgentType,EdgeType> cannot be applied to given types;
this.region.getNetworkService().removeNode(this.region.getNetwork(), this);
^
required: MoreNetwork<SocialAgent,CAP#1>,SocialAgent
found: MoreNetwork<SocialAgent,MoreEdge<SocialAgent>>,DefaultSocialLandUseAgent
reason: argument mismatch; MoreNetwork<SocialAgent,MoreEdge<SocialAgent>> cannot be converted to MoreNetwork<SocialAgent,CAP#1>
where AgentType,EdgeType are type-variables:
AgentType extends Object declared in interface MoreNetworkModifier
EdgeType extends MoreEdge<? super AgentType> declared in interface MoreNetworkModifier
where CAP#1 is a fresh type-variable:
CAP#1 extends MoreEdge<SocialAgent> from capture of ? extends MoreEdge<SocialAgent>
1 error
为了完整起见,包含违规行的方法是:
public void die() {
if (this.region.getNetworkService() != null && this.region.getNetwork() != null) {
this.region.getNetworkService().removeNode(this.region.getNetwork(), this);
}
if (this.region.getGeography() != null
&& this.region.getGeography().getGeometry(this) != null) {
this.region.getGeography().move(this, null);
}
}
错误信息分析
- 我们被告知编译器在需要
MoreNetwork<SocialAgent,CAP#1>
的地方找到了类型 MoreNetwork<SocialAgent,MoreEdge<SocialAgent>>
。
- 这意味着
CAP#1
的推断值与 MoreEdge<SocialAgent>
不兼容
- 但是我们被告知
CAP#1 extends MoreEdge<SocialAgent> from capture of ? extends MoreEdge<SocialAgent>
Java documentation on Upper Bounded Wildcards 表示
The upper bounded wildcard, <? extends Foo>
, where Foo
is any type, matches Foo
and any subtype of Foo
.
我不明白为什么 MoreEdge<SocialAgent>
不匹配 <? extends MoreEdge<SocialAgent>>
,因此无法协调 2 和 3。
解决问题的努力
虽然我的 objective 是为 Java 8 编译的,但我知道在 javac
中发现了过去与泛型和通配符相关的错误(参见 ).但是,我发现 javac 1.8.0_275 和 javac 14.0.2.
都存在同样的问题
我还考虑过某种形式的显式转换是否可以为编译器提供足够的提示。但是我想不出要更改什么,因为 this.region.getNetwork()
的类型在错误消息中按预期报告为 MoreNetwork<SocialAgent,MoreEdge<SocialAgent>>
。
问题的概括(编辑)
@rzwitserloot 正确地指出,我没有在上面的代码中包含足够的有关依赖项的信息来正确调试。复制所有依赖项(包括我无法控制的库中的一些代码)会变得非常混乱,因此我将问题提炼到一个产生类似错误的独立程序中。
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
public class UpperBoundNestedGenericsDemo {
public static void main(String[] args) {
MapContainerManagerBroken mapContainerManager = new MapContainerManagerBroken();
MapContainer<A, B<A>> mapContainer = new MapContainer<>();
mapContainerManager.setMapContainer(mapContainer);
Map<A, B<A>> aMap = new HashMap<>();
aMap.put(new A(), new B<A>());
mapContainerManager.getMapContainer().addMap(aMap);
mapContainerManager.getMapContainer().removeMap(aMap);
}
}
/**
* Analogue of Region
*/
class MapContainerManagerBroken {
private MapContainer<A, ? extends B<A>> mapContainer;
void setMapContainer(MapContainer<A, ? extends B<A>> mapContainer) {
this.mapContainer = mapContainer;
}
MapContainer<A, ? extends B<A>> getMapContainer() {
return this.mapContainer;
}
}
/**
* Analogue of MoreNetworkService
*/
class MapContainer<T1, T2> {
List<Map<T1, T2>> listOfMaps = new ArrayList<>();
void addMap(Map<T1, T2> map) {
listOfMaps.add(map);
}
boolean removeMap(Map<T1, T2> map) {
return listOfMaps.remove(map);
}
}
class A {
}
class B<T> {
}
这在 Eclipse 中编译,但在使用
编译时
javac -Xdiags:verbose UpperBoundNestedGenericsDemo.java
产生错误信息
UpperBoundNestedGenericsDemo.java:18: error: method addMap in class MapContainer<T1,T2> cannot be applied to given types;
mapContainerManager.getMapContainer().addMap(aMap);
^
required: Map<A,CAP#1>
found: Map<A,B<A>>
reason: argument mismatch; Map<A,B<A>> cannot be converted to Map<A,CAP#1>
where T1,T2 are type-variables:
T1 extends Object declared in class MapContainer
T2 extends Object declared in class MapContainer
where CAP#1 is a fresh type-variable:
CAP#1 extends B<A> from capture of ? extends B<A>
UpperBoundNestedGenericsDemo.java:19: error: method removeMap in class MapContainer<T1,T2> cannot be applied to given types;
mapContainerManager.getMapContainer().removeMap(aMap);
^
required: Map<A,CAP#1>
found: Map<A,B<A>>
reason: argument mismatch; Map<A,B<A>> cannot be converted to Map<A,CAP#1>
where T1,T2 are type-variables:
T1 extends Object declared in class MapContainer
T2 extends Object declared in class MapContainer
where CAP#1 is a fresh type-variable:
CAP#1 extends B<A> from capture of ? extends B<A>
2 errors
部分解决方案
可以修改上一节中的程序,将 MapContainerManagerBroken
替换为
,使其在 Eclipse 和 javac
下都能编译
class MapContainerManagerNoWildcards {
private MapContainer<A, B<A>> mapContainer;
void setMapContainer(MapContainer<A, B<A>> mapContainer) {
this.mapContainer = mapContainer;
}
MapContainer<A, B<A>> getMapContainer() {
return this.mapContainer;
}
}
也就是说,通过删除 MapContainer
的通配符类型边界。这解决了眼前的实际问题,但与 MapContainerManagerBroken
相比限制了 MapContainerManagerNoWildcards
的灵活性。另一种方法是使这个 class 通用,例如
class MapContainerManagerFixedGeneric<T extends B<A>> {
private MapContainer<A, T> mapContainer;
void setMapContainer(MapContainer<A, T> mapContainer) {
this.mapContainer = mapContainer;
}
MapContainer<A, T> getMapContainer() {
return this.mapContainer;
}
}
但是,这并不能解释为什么当 mapContainerManager
是 MapContainerManagerBroken
时行 mapContainerManager.getMapContainer().addMap(aMap)
是编译器错误(例如在示例程序中)。具体来说,为什么前面一行报错,后面编译通过?
MapContainer<A, ? extends B<A>> mapContainer = new MapContainer<A, B<A>>();
就类型兼容性而言,您误解了有关 ? extends
含义的规则。
任意两次 ? extends Number
彼此不兼容,? extends Number
也不与 Number
本身兼容。这是一个简单的 'proof' 原因:
List<Integer> ints = new ArrayList<Integer>();
List<? extends Number> numbers1 = ints; // legal
List<Number> numbers2 = numbers1; // legal?
numbers2.add(new Double(5.0)); // oh whoopsie
如果上面编译成功,那么ints
中有一个非int,那是不行的。幸运的是,它没有编译。具体来说,第三行是编译错误。
您所说的 JLS 路段是指单行道。您可以将 List<Number>
分配给类型为 List<? extends Number>
的变量(或者当参数是该类型时将 List<Number>
作为参数传递),但反之则不行。
?
等同于 'imagine I used a letter here and that I use this letter only here and nowhere else'。因此,如果涉及两个 ?
,它们可能彼此不相等。因此,出于类型兼容性的目的,它们是不兼容的。这是有道理的;假设你有:
void foo(List<? extends Number> a, List<? extends Number b>) {}
那么按理说,我可以调用这个传递一些 List<Integer>
给 a 和 List<Double>
给 b:每个 ?
都可以成为它想要的任何东西只要它符合界限。这也意味着 不可能 在这些列表中的任何一个上调用 add
,因为您添加的内容必须是 ?
类型,而您不能做到这一点(除了,平凡地,通过写 .add(null)
,因为 null 是用于这种目的的每种类型),但这不是很有用)。它还解释了为什么你不能写 a = b;
,这就是你问题的核心。为什么不能将 a
分配给 b
?毕竟他们是同一类型的! - 不,它们不是,CAP 的东西捕获了这一点:a 是 CAP#1
类型,b 是 CAP#2
类型。这就是 javac(和 ecj,大概)解决这个问题的方式,这就是为什么这个 CAP 东西出现的原因。这不是编译器故意密集或规格不足的问题。它是泛型复杂性所固有的。
因此,是的:CAP#1
与 ? extends Number
不同,它只是其中的一个捕获(并且任何进一步的 ? extends Number
将被称为 CAP#2
,并且 CAP#1 和 CAP#2 不兼容;毕竟一个可能是 Integer,一个可能是 Double)。错误消息本身是合理的。
通常如果 ecj
和 javac
不一致,通常 ecj
是正确的而 javac 不是,根据个人经验(我在 运行 中叙述了大约 10 次在 ecj 和 javac 不同意的情况下,10 次中有 9 次,ecj 比 javac 更正确;尽管我随后报告的 JLS 中经常出现歧义,并且已经解决)。尽管如此,考虑到 JDK14 仍然存在问题,并尝试解释这些错误消息(如果没有此处涉及的所有签名,这将非常困难,您还没有粘贴代码库的有用部分),它看起来确实像 javac
正确。
通常的解决方法是在里面扔更多 ?
。特别是,removeEdge
肯定听起来应该接受 Object
或 ? extends T
而不是 T
。毕竟,arraylist 的 .remove()
方法接受任何对象,而不是 T
- 尝试从 int 列表中删除一些 double 根本不会做任何事情,按照规范:Asking a list to remove a thing那不在里面是一个空洞。那么没有理由限制参数。这解决了很多问题。
编辑,在你用更详细的方式显着更新你的问题之后。
MapContainer<A, ? extends B> mapContainer = new MapContainer<A, B>();
因为这就是那个意思。请记住,MapContainer<? extends Number>
不代表类型。它代表了整个维度的类型。它是说 mapContainer
是一个几乎可以指向任何东西的引用,只要它是一个 MapContainer,任何 'tag' 的东西(请 <>
中的东西,只要东西介于两者之间的是 Number 或其任何子类型。您可以在此展开的 'could be so many things' 类型上调用的唯一方法是 ALL 它可能在命令中拥有的东西,并且没有 addMap
任何条带的方法是交集 的一部分。addMap
方法的参数涉及 A
,如在本例中,与 ? extends Number
相同,编译器说:好吧,我不知道。没有适合的类型。我不能使用 Number
;如果你有一个 MapContainer<Integer>
? 如果我让你使用任何 Number
调用 addMap
,你可以在其中放置一个 Double,这是不允许的。eclipse 允许它的事实很奇怪。
这是一个边缘性的小例子:
Map<? extends Number, String> x = ...;
x.put(A, B);
在上面的例子中,nothing 可以写在 3 个点上,或者代替 A
使 ever compile。 ? extends
是 shorthand 因为:否 add/put。期间.
实际上只有一种方法可以工作:x.put(null, B);
,因为每种类型都为 null 'fits'。但这是一种逃避,对严肃的代码一点用处都没有。
一旦你完全理解了这一点,问题就得到了解释。更一般地说,假设你有一个 MapContainer<? extends something>
,你不能在那个东西上调用 addMap。时期。您不能在 extends
样式类型边界上调用 'write' 操作。
我已经解释了为什么这是这个答案的最顶部。
我使用的代码库一直依赖 Eclipse 进行编译,直到现在。我的objective是用javac
(通过ant
)编译它来简化构建过程。该项目在 Eclipse(版本 2019-12(4.14.0))中编译无投诉,但 javac
(OpenJDK,版本 1.8.0_275 和 14.0.2)产生 method ... cannot be applied to given types
错误涉及上限通配符。
重现步骤
请注意,在撰写本文时,存储库为 64 MB:
git clone git@github.com:jamesdamillington/CRAFTY_Brazil.git
cd CRAFTY_Brazil && git checkout 550e88e
javac -Xdiags:verbose \
-classpath bin:lib/jts-1.13.jar:lib/MORe.jar:lib/ParMa.jar:lib/ModellingUtilities.jar:lib/log4j-1.2.17.jar:lib/repast.simphony.bin_and_src.jar \
src/org/volante/abm/agent/DefaultSocialLandUseAgent.java
错误信息输出:
src/org/volante/abm/agent/DefaultSocialLandUseAgent.java:166: error: method removeNode in interface MoreNetworkModifier<AgentType,EdgeType> cannot be applied to given types;
this.region.getNetworkService().removeNode(this.region.getNetwork(), this);
^
required: MoreNetwork<SocialAgent,CAP#1>,SocialAgent
found: MoreNetwork<SocialAgent,MoreEdge<SocialAgent>>,DefaultSocialLandUseAgent
reason: argument mismatch; MoreNetwork<SocialAgent,MoreEdge<SocialAgent>> cannot be converted to MoreNetwork<SocialAgent,CAP#1>
where AgentType,EdgeType are type-variables:
AgentType extends Object declared in interface MoreNetworkModifier
EdgeType extends MoreEdge<? super AgentType> declared in interface MoreNetworkModifier
where CAP#1 is a fresh type-variable:
CAP#1 extends MoreEdge<SocialAgent> from capture of ? extends MoreEdge<SocialAgent>
1 error
为了完整起见,包含违规行的方法是:
public void die() {
if (this.region.getNetworkService() != null && this.region.getNetwork() != null) {
this.region.getNetworkService().removeNode(this.region.getNetwork(), this);
}
if (this.region.getGeography() != null
&& this.region.getGeography().getGeometry(this) != null) {
this.region.getGeography().move(this, null);
}
}
错误信息分析
- 我们被告知编译器在需要
MoreNetwork<SocialAgent,CAP#1>
的地方找到了类型MoreNetwork<SocialAgent,MoreEdge<SocialAgent>>
。 - 这意味着
CAP#1
的推断值与MoreEdge<SocialAgent>
不兼容
- 但是我们被告知
CAP#1 extends MoreEdge<SocialAgent> from capture of ? extends MoreEdge<SocialAgent>
Java documentation on Upper Bounded Wildcards 表示
The upper bounded wildcard,
<? extends Foo>
, whereFoo
is any type, matchesFoo
and any subtype ofFoo
.
我不明白为什么 MoreEdge<SocialAgent>
不匹配 <? extends MoreEdge<SocialAgent>>
,因此无法协调 2 和 3。
解决问题的努力
虽然我的 objective 是为 Java 8 编译的,但我知道在 javac
中发现了过去与泛型和通配符相关的错误(参见
我还考虑过某种形式的显式转换是否可以为编译器提供足够的提示。但是我想不出要更改什么,因为 this.region.getNetwork()
的类型在错误消息中按预期报告为 MoreNetwork<SocialAgent,MoreEdge<SocialAgent>>
。
问题的概括(编辑)
@rzwitserloot 正确地指出,我没有在上面的代码中包含足够的有关依赖项的信息来正确调试。复制所有依赖项(包括我无法控制的库中的一些代码)会变得非常混乱,因此我将问题提炼到一个产生类似错误的独立程序中。
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
public class UpperBoundNestedGenericsDemo {
public static void main(String[] args) {
MapContainerManagerBroken mapContainerManager = new MapContainerManagerBroken();
MapContainer<A, B<A>> mapContainer = new MapContainer<>();
mapContainerManager.setMapContainer(mapContainer);
Map<A, B<A>> aMap = new HashMap<>();
aMap.put(new A(), new B<A>());
mapContainerManager.getMapContainer().addMap(aMap);
mapContainerManager.getMapContainer().removeMap(aMap);
}
}
/**
* Analogue of Region
*/
class MapContainerManagerBroken {
private MapContainer<A, ? extends B<A>> mapContainer;
void setMapContainer(MapContainer<A, ? extends B<A>> mapContainer) {
this.mapContainer = mapContainer;
}
MapContainer<A, ? extends B<A>> getMapContainer() {
return this.mapContainer;
}
}
/**
* Analogue of MoreNetworkService
*/
class MapContainer<T1, T2> {
List<Map<T1, T2>> listOfMaps = new ArrayList<>();
void addMap(Map<T1, T2> map) {
listOfMaps.add(map);
}
boolean removeMap(Map<T1, T2> map) {
return listOfMaps.remove(map);
}
}
class A {
}
class B<T> {
}
这在 Eclipse 中编译,但在使用
编译时javac -Xdiags:verbose UpperBoundNestedGenericsDemo.java
产生错误信息
UpperBoundNestedGenericsDemo.java:18: error: method addMap in class MapContainer<T1,T2> cannot be applied to given types;
mapContainerManager.getMapContainer().addMap(aMap);
^
required: Map<A,CAP#1>
found: Map<A,B<A>>
reason: argument mismatch; Map<A,B<A>> cannot be converted to Map<A,CAP#1>
where T1,T2 are type-variables:
T1 extends Object declared in class MapContainer
T2 extends Object declared in class MapContainer
where CAP#1 is a fresh type-variable:
CAP#1 extends B<A> from capture of ? extends B<A>
UpperBoundNestedGenericsDemo.java:19: error: method removeMap in class MapContainer<T1,T2> cannot be applied to given types;
mapContainerManager.getMapContainer().removeMap(aMap);
^
required: Map<A,CAP#1>
found: Map<A,B<A>>
reason: argument mismatch; Map<A,B<A>> cannot be converted to Map<A,CAP#1>
where T1,T2 are type-variables:
T1 extends Object declared in class MapContainer
T2 extends Object declared in class MapContainer
where CAP#1 is a fresh type-variable:
CAP#1 extends B<A> from capture of ? extends B<A>
2 errors
部分解决方案
可以修改上一节中的程序,将 MapContainerManagerBroken
替换为
javac
下都能编译
class MapContainerManagerNoWildcards {
private MapContainer<A, B<A>> mapContainer;
void setMapContainer(MapContainer<A, B<A>> mapContainer) {
this.mapContainer = mapContainer;
}
MapContainer<A, B<A>> getMapContainer() {
return this.mapContainer;
}
}
也就是说,通过删除 MapContainer
的通配符类型边界。这解决了眼前的实际问题,但与 MapContainerManagerBroken
相比限制了 MapContainerManagerNoWildcards
的灵活性。另一种方法是使这个 class 通用,例如
class MapContainerManagerFixedGeneric<T extends B<A>> {
private MapContainer<A, T> mapContainer;
void setMapContainer(MapContainer<A, T> mapContainer) {
this.mapContainer = mapContainer;
}
MapContainer<A, T> getMapContainer() {
return this.mapContainer;
}
}
但是,这并不能解释为什么当 mapContainerManager
是 MapContainerManagerBroken
时行 mapContainerManager.getMapContainer().addMap(aMap)
是编译器错误(例如在示例程序中)。具体来说,为什么前面一行报错,后面编译通过?
MapContainer<A, ? extends B<A>> mapContainer = new MapContainer<A, B<A>>();
就类型兼容性而言,您误解了有关 ? extends
含义的规则。
任意两次 ? extends Number
彼此不兼容,? extends Number
也不与 Number
本身兼容。这是一个简单的 'proof' 原因:
List<Integer> ints = new ArrayList<Integer>();
List<? extends Number> numbers1 = ints; // legal
List<Number> numbers2 = numbers1; // legal?
numbers2.add(new Double(5.0)); // oh whoopsie
如果上面编译成功,那么ints
中有一个非int,那是不行的。幸运的是,它没有编译。具体来说,第三行是编译错误。
您所说的 JLS 路段是指单行道。您可以将 List<Number>
分配给类型为 List<? extends Number>
的变量(或者当参数是该类型时将 List<Number>
作为参数传递),但反之则不行。
?
等同于 'imagine I used a letter here and that I use this letter only here and nowhere else'。因此,如果涉及两个 ?
,它们可能彼此不相等。因此,出于类型兼容性的目的,它们是不兼容的。这是有道理的;假设你有:
void foo(List<? extends Number> a, List<? extends Number b>) {}
那么按理说,我可以调用这个传递一些 List<Integer>
给 a 和 List<Double>
给 b:每个 ?
都可以成为它想要的任何东西只要它符合界限。这也意味着 不可能 在这些列表中的任何一个上调用 add
,因为您添加的内容必须是 ?
类型,而您不能做到这一点(除了,平凡地,通过写 .add(null)
,因为 null 是用于这种目的的每种类型),但这不是很有用)。它还解释了为什么你不能写 a = b;
,这就是你问题的核心。为什么不能将 a
分配给 b
?毕竟他们是同一类型的! - 不,它们不是,CAP 的东西捕获了这一点:a 是 CAP#1
类型,b 是 CAP#2
类型。这就是 javac(和 ecj,大概)解决这个问题的方式,这就是为什么这个 CAP 东西出现的原因。这不是编译器故意密集或规格不足的问题。它是泛型复杂性所固有的。
因此,是的:CAP#1
与 ? extends Number
不同,它只是其中的一个捕获(并且任何进一步的 ? extends Number
将被称为 CAP#2
,并且 CAP#1 和 CAP#2 不兼容;毕竟一个可能是 Integer,一个可能是 Double)。错误消息本身是合理的。
通常如果 ecj
和 javac
不一致,通常 ecj
是正确的而 javac 不是,根据个人经验(我在 运行 中叙述了大约 10 次在 ecj 和 javac 不同意的情况下,10 次中有 9 次,ecj 比 javac 更正确;尽管我随后报告的 JLS 中经常出现歧义,并且已经解决)。尽管如此,考虑到 JDK14 仍然存在问题,并尝试解释这些错误消息(如果没有此处涉及的所有签名,这将非常困难,您还没有粘贴代码库的有用部分),它看起来确实像 javac
正确。
通常的解决方法是在里面扔更多 ?
。特别是,removeEdge
肯定听起来应该接受 Object
或 ? extends T
而不是 T
。毕竟,arraylist 的 .remove()
方法接受任何对象,而不是 T
- 尝试从 int 列表中删除一些 double 根本不会做任何事情,按照规范:Asking a list to remove a thing那不在里面是一个空洞。那么没有理由限制参数。这解决了很多问题。
编辑,在你用更详细的方式显着更新你的问题之后。
MapContainer<A, ? extends B> mapContainer = new MapContainer<A, B>();
因为这就是那个意思。请记住,MapContainer<? extends Number>
不代表类型。它代表了整个维度的类型。它是说 mapContainer
是一个几乎可以指向任何东西的引用,只要它是一个 MapContainer,任何 'tag' 的东西(请 <>
中的东西,只要东西介于两者之间的是 Number 或其任何子类型。您可以在此展开的 'could be so many things' 类型上调用的唯一方法是 ALL 它可能在命令中拥有的东西,并且没有 addMap
任何条带的方法是交集 的一部分。addMap
方法的参数涉及 A
,如在本例中,与 ? extends Number
相同,编译器说:好吧,我不知道。没有适合的类型。我不能使用 Number
;如果你有一个 MapContainer<Integer>
? 如果我让你使用任何 Number
调用 addMap
,你可以在其中放置一个 Double,这是不允许的。eclipse 允许它的事实很奇怪。
这是一个边缘性的小例子:
Map<? extends Number, String> x = ...;
x.put(A, B);
在上面的例子中,nothing 可以写在 3 个点上,或者代替 A
使 ever compile。 ? extends
是 shorthand 因为:否 add/put。期间.
实际上只有一种方法可以工作:x.put(null, B);
,因为每种类型都为 null 'fits'。但这是一种逃避,对严肃的代码一点用处都没有。
一旦你完全理解了这一点,问题就得到了解释。更一般地说,假设你有一个 MapContainer<? extends something>
,你不能在那个东西上调用 addMap。时期。您不能在 extends
样式类型边界上调用 'write' 操作。
我已经解释了为什么这是这个答案的最顶部。