简体   繁体   English

在 Java 8 中构建时的“未定义引用:.. ConcurrentHashMap.keySet()”

[英]“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.

相关问题 Java ConcurrentHashMap.keySet()挂起 - Java ConcurrentHashMap.keySet() hangs ConcurrentHashMap.keySet()。removeAll()性能问题 - ConcurrentHashMap.keySet().removeAll() performance issue ConcurrentHashMap.keySet().stream() 的一致性行为 - Consistency behaviour of ConcurrentHashMap.keySet().stream() ProcessSimulator.killAllProcesses(line:78)NoSuchMethodError ConcurrentHashMap.keySet() - ProcessSimulator.killAllProcesses(line:78) NoSuchMethodError ConcurrentHashMap.keySet() Java错误java.util.concurrent.ConcurrentHashMap.keySet - Java error java.util.concurrent.ConcurrentHashMap.keySet 如何在Java中创建不区分大小写的键集ConcurrentHashMap? - How can I make a case insensitive keyset ConcurrentHashMap in java? Tomcat 7 NoSuchMethodError:java.util.concurrent.ConcurrentHashMap.keySet() - Tomcat 7 NoSuchMethodError: java.util.concurrent.ConcurrentHashMap.keySet() ConcurrentHashMap的KeySet迭代器是线程安全的吗? - Is KeySet iterator of ConcurrentHashMap is threadsafe? 修改映射并迭代键集时,Java ConcurentMap keySet()问题 - Java ConcurentMap keySet() question when map is modified and iterating over keyset Java 中的 ConcurrentHashMap 类型未定义方法 split(String) - Method split(String) is undefined for the type ConcurrentHashMap in Java
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM