[英]“Undefined reference: .. ConcurrentHashMap.keySet()” when building in Java 8
i have a project, and i am build this project with jdk 6,7,8 and my target is 1.6我有一个项目,我正在用 jdk 6、7、8 构建这个项目,我的目标是 1.6
when i build jdk 8 i get this error:当我构建 jdk 8 时,出现此错误:
Undefined reference: java.util.concurrent.ConcurrentHashMap.KeySetView java.util.concurrent.ConcurrentHashMap.keySet()
since i have this code in that line:因为我在该行中有此代码:
final Iterator<CLASS_NAME> itr = hashMap.keySet().iterator();
how can avoid error, i made some search in internet, and since java 8 changed its return type keyset, i got error.如何避免错误,我在互联网上进行了一些搜索,由于 Java 8 更改了其返回类型键集,因此出现错误。 is this any solution.
这是任何解决方案。 i am using maven, and animal-sniffer-plugin gives this error, with signature error.
我正在使用 maven,animal-sniffer-plugin 给出了这个错误,签名错误。
You should be able to use chm.keySet()
with ConcurrentHashMap
.您应该能够将
chm.keySet()
与ConcurrentHashMap
一起使用。
Between Java 7 and Java 8, the ConcurrentHashMap.keySet()
method did change from returning Set<K>
to returning ConcurrentHashMap.KeySetView<K,V>
.在 Java 7 和 Java 8 之间,
ConcurrentHashMap.keySet()
方法确实从返回Set<K>
更改为返回ConcurrentHashMap.KeySetView<K,V>
。 This is a covariant override , since KeySetView<K,V>
implements Set<K>
.这是一个协变覆盖,因为
KeySetView<K,V>
实现了Set<K>
。 It's also both source and binary compatible.它也是源代码和二进制兼容的。 That is, the same source code should work fine when built and run on 7 and when built and run on 8. The binary built on 7 should also run on 8.
也就是说,在 7 上构建和运行时以及在 8 上构建和运行时,相同的源代码应该可以正常工作。 7 上构建的二进制文件也应该在 8 上运行。
Why the undefined reference?为什么是未定义的引用? I suspect there is a build configuration problem that mixes bits from Java 7 and Java 8. Typically this occurs because the when attempting to compile for a previous release, the
-source
and -target
options are specified, but the -bootclasspath
option isn't specified.我怀疑存在混合了 Java 7 和 Java 8 位的构建配置问题。通常发生这种情况是因为在尝试为以前的版本编译时,指定了
-source
和-target
选项,但未指定-bootclasspath
选项指定的。 For example, consider the following:例如,请考虑以下情况:
/path/to/jdk8/bin/javac -source 1.7 -target 1.7 MyClass.java
If MyClass.java contains any dependencies on JDK 8 APIs, this will not work when run using JDK 7;如果 MyClass.java 包含对 JDK 8 API 的任何依赖,则在使用 JDK 7 运行时这将不起作用; it will result in a
NoSuchMethodError
being thrown at runtime.它会导致在运行时
NoSuchMethodError
。
Note that this error won't occur immediately;请注意,此错误不会立即发生; it will occur only when an attempt is made to invoke the method in question.
它只会在尝试调用相关方法时发生。 This is because linkage in Java is done lazily.
这是因为 Java 中的链接是惰性完成的。 Thus, you can have references to nonexistent methods lurking around in your code for an arbitrary amount of time, and errors won't occur unless the execution path attempts to invoke such a method.
因此,您可以引用不存在的方法在任意时间内潜伏在您的代码中,并且除非执行路径尝试调用这样的方法,否则不会发生错误。 (This is both a blessing and a curse.)
(这既是福也是祸。)
It's not always easy to discern dependencies on newer JDK APIs by looking at the source code.通过查看源代码来辨别对较新 JDK API 的依赖并不总是那么容易。 In this case, if you compile using JDK 8, the call to
keySet()
will compile in a reference to the method that returns a ConcurrentHashMap.KeySetView
because that's the method that's found in the JDK 8 class library.在这种情况下,如果您使用 JDK 8 进行编译,则对
keySet()
的调用将在对返回ConcurrentHashMap.KeySetView
的方法的引用中进行编译,因为这是在 JDK 8 类库中找到的方法。 The -target 1.7
option makes the resulting class file compatible with JDK 7, and the -source 1.7
option limits the language level to JDK 7 (which doesn't apply in this case). -target 1.7
选项使生成的类文件与 JDK 7 兼容,而-source 1.7
选项将语言级别限制为 JDK 7(在这种情况下不适用)。 But the result is actually neither fish nor fowl: the class file format will work with JDK 7, but it contains references to the JDK 8 class library .但结果实际上既非鱼也非家禽:类文件格式将适用于 JDK 7,但它包含对 JDK 8类库的引用。 If you try to run this on JDK 7, of course it can't find the new stuff introduced in 8, so the error occurs.
如果你尝试在JDK 7上运行这个,当然找不到8中引入的新东西,所以会出现错误。
You can attempt to work around this by modifying the source code to avoid dependencies on newer JDK APIs.您可以尝试通过修改源代码来解决此问题,以避免依赖较新的 JDK API。 So if your code is this:
所以如果你的代码是这样的:
ConcurrentHashMap<K, V> hashMap = ... ;
final Iterator<CLASS_NAME> itr = hashMap.keySet().iterator();
You could change it to this:你可以把它改成这样:
ConcurrentHashMap<K, V> hashMap = ... ;
final Iterator<CLASS_NAME> itr = ((Map<K, V>)hashMap).keySet().iterator();
This can be made to work, but I don't recommend it.这可以工作,但我不推荐它。 First, it garbages up your code.
首先,它会破坏你的代码。 Second, it's not at all obvious where changes like this need to be applied, so it's hard to tell when you've gotten all the cases.
其次,需要应用此类更改的地方并不明显,因此很难判断您何时获得了所有案例。 Third, it's hard to maintain, as the reason for this cast isn't at all obvious and is easily refactored away.
第三,很难维护,因为这个转换的原因根本不明显,而且很容易重构。
The correct solution is to make sure you have a build environment that is purely based on the oldest JDK version you want to support.正确的解决方案是确保您拥有一个完全基于您想要支持的最旧 JDK 版本的构建环境。 The binary should then run unchanged on later JDKs.
然后二进制文件应该在以后的 JDK 上运行不变。 For example, to compile for JDK 7 using JDK 8, do this:
例如,要使用 JDK 8 为 JDK 7 进行编译,请执行以下操作:
/path/to/jdk8/bin/javac -source 1.7 -target 1.7 -bootclasspath /path/to/jdk7/jre/lib/rt.jar MyClass.java
In addition to specifying the -source
and -target
options, specifying the -bootclasspath
option will limit all dependencies to the APIs found at that location, which of course must match the version specified for the other options.除了指定
-source
和-target
选项之外,指定-bootclasspath
选项会将所有依赖项限制为在该位置找到的 API,当然这些 API 必须与为其他选项指定的版本相匹配。 This will prevent any inadvertent dependencies on JDK 8 from creeping into the resulting binaries.这将防止对 JDK 8 的任何无意依赖蔓延到生成的二进制文件中。
In JDK 9 and later, a new option --release
has been added.在 JDK 9 及更高版本中,添加了一个新选项
--release
。 This effectively sets the source, target, and bootclasspath to the same JDK platform level all at once.这有效地一次性将源、目标和引导类路径设置为相同的 JDK 平台级别。 For example:
例如:
/path/to/jdk9/bin/javac --release 7 MyClass.java
When using the --release
option, it's no longer necessary to have the older JDK's rt.jar
around for build purposes, as javac
includes tables of public APIs for earlier releases.使用
--release
选项时,不再需要将旧版 JDK 的rt.jar
用于构建目的,因为javac
包括早期版本的公共 API 表。
** **
More generally, this kind of issue tends to occur when the JDK introduces covariant overrides in a new JDK release.更一般地,当 JDK 在新的 JDK 版本中引入协变覆盖时,往往会发生这种问题。 This happened in JDK 9 with the various
Buffer
subclasses.这发生在带有各种
Buffer
子类的 JDK 9 中。 For example, ByteBuffer.limit(int)
was overridden and now returns ByteBuffer
, whereas in JDK 8 and earlier this method was inherited from Buffer
and returned Buffer
.例如,
ByteBuffer.limit(int)
被覆盖并现在返回ByteBuffer
,而在 JDK 8 及更早版本中,此方法是从Buffer
继承并返回Buffer
。 (Several other covariant overrides were added in this area.) Systems compiled on JDK 9 using only -source 8 -target 8
will run into exactly the same issue with NoSuchMethodError
. (在该区域中添加了其他几个协变覆盖。)仅使用
-source 8 -target 8
在 JDK 9 上编译的系统将遇到与NoSuchMethodError
完全相同的问题。 The solution is the same: use --release 8
.解决方案是一样的:使用
--release 8
。
Another Answer suggests a modification to your code (using keys()
instead of keySet()
) so that you can compile your source code on Java 8 and run on Java 7. I think that is a retrograde step.另一个答案建议修改您的代码(使用
keys()
而不是keySet()
),以便您可以在 Java 8 上编译源代码并在 Java 7 上运行。我认为这是一个倒退的步骤。
Instead:反而:
If your aim is to create a production build of your software that will run on Java 6, 7 and 8, then your best bet is to do your production builds on JDK 6.如果您的目标是创建将在 Java 6、7 和 8 上运行的软件的生产版本,那么最好的办法是在 JDK 6 上进行生产版本。
If your aim is to do your development builds on Java 8 (but stay backwards compatible at the source code level for now), then change the maven plugin configs for animal-sniffer to ignore these classes;如果您的目标是在 Java 8 上进行开发(但现在在源代码级别保持向后兼容),那么更改动物嗅探器的 maven 插件配置以忽略这些类; see http://mojo.codehaus.org/animal-sniffer-maven-plugin/examples/checking-signatures.html for an explanation.
有关解释,请参阅http://mojo.codehaus.org/animal-sniffer-maven-plugin/examples/checking-signatures.html 。
However, there is a risk that animal-sniffer will ignore too much;然而,动物嗅探器有可能忽略太多; eg it won't tell you if you use new Java 8 methods in
ConcurrentHashMap
.例如,它不会告诉您是否在
ConcurrentHashMap
使用了新的Java 8 方法。 You will need to allow for that possibility ....你需要考虑到这种可能性......
If your aim is to move to Java 8 (so that you can start using new Java 8 features in your code), then just do it.如果您的目标是迁移到 Java 8(以便您可以开始在代码中使用新的 Java 8 功能),那么就去做吧。 Your code won't be backward compatible, but you can't support old versions of Java for ever ...
您的代码不会向后兼容,但您不能永远支持旧版本的 Java...
(These suggestions are not mutually exclusive, if you consider the big picture.) (如果您考虑大局,这些建议并不相互排斥。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.