【问题标题】:"Undefined reference: .. ConcurrentHashMap.keySet()" when building in Java 8“未定义的引用:.. ConcurrentHashMap.keySet()”在 Java 8 中构建时
【发布时间】:2014-09-06 22:15:01
【问题描述】:

我有一个项目,我正在用 jdk 6,7,8 构建这个项目,我的目标是 1.6

当我构建 jdk 8 时出现此错误:

Undefined reference: java.util.concurrent.ConcurrentHashMap.KeySetView java.util.concurrent.ConcurrentHashMap.keySet()

因为我在那一行中有这段代码:

   final Iterator<CLASS_NAME> itr = hashMap.keySet().iterator();

如何避免错误,我在互联网上进行了一些搜索,由于 java 8 更改了它的返回类型键集,我得到了错误。这是任何解决方案。 我正在使用 maven,而 animal-sniffer-plugin 给出了这个错误,带有签名错误。

【问题讨论】:

标签: java java-8 concurrenthashmap


【解决方案1】:

您应该能够将chm.keySet()ConcurrentHashMap 一起使用。

在 Java 7 和 Java 8 之间,ConcurrentHashMap.keySet() 方法确实从返回 Set&lt;K&gt; 变为返回 ConcurrentHashMap.KeySetView&lt;K,V&gt;。这是一个协变覆盖,因为KeySetView&lt;K,V&gt; 实现了Set&lt;K&gt;。它也是源代码和二进制兼容的。也就是说,相同的源代码在 7 上构建和运行以及在 8 上构建和运行时应该可以正常工作。在 7 上构建的二进制文件也应该在 8 上运行。

为什么是未定义的引用?我怀疑存在混合来自 Java 7 和 Java 8 的位的构建配置问题。通常发生这种情况是因为在尝试为以前的版本编译时,指定了 -source-target 选项,但 -bootclasspath 选项未指定。例如,考虑以下情况:

/path/to/jdk8/bin/javac -source 1.7 -target 1.7 MyClass.java

如果 MyClass.java 包含对 JDK 8 API 的任何依赖项,则在使用 JDK 7 运行时,此将不起作用;这将导致在运行时抛出NoSuchMethodError

请注意,此错误不会立即发生;只有在尝试调用相关方法时才会发生这种情况。这是因为 Java 中的链接是延迟完成的。因此,您可以在任意时间内引用潜伏在代码中的不存在的方法,并且除非执行路径尝试调用此类方法,否则不会发生错误。 (这既是福也是祸。)

通过查看源代码来辨别对较新 JDK API 的依赖并不总是那么容易。在这种情况下,如果您使用 JDK 8 进行编译,对 keySet() 的调用将在对返回 ConcurrentHashMap.KeySetView 的方法的引用中进行编译,因为这是在 JDK 8 类库中找到的方法。 -target 1.7 选项使生成的类文件与 JDK 7 兼容,-source 1.7 选项将语言级别限制为 JDK 7(在这种情况下不适用)。但结果实际上不伦不类:类文件格式将适用于 JDK 7,但它包含对 JDK 8 类库的引用。如果你尝试在JDK 7上运行这个,当然找不到8中引入的新东西,所以出现错误。

您可以尝试通过修改源代码来解决此问题,以避免依赖于较新的 JDK API。所以如果你的代码是这样的:

ConcurrentHashMap<K, V> hashMap = ... ;
final Iterator<CLASS_NAME> itr = hashMap.keySet().iterator();

你可以改成这样:

ConcurrentHashMap<K, V> hashMap = ... ;
final Iterator<CLASS_NAME> itr = ((Map<K, V>)hashMap).keySet().iterator();

这可以工作,但我不推荐它。首先,它会破坏您的代码。其次,像这样的改变需要应用在哪里并不明显,所以很难说你什么时候得到了所有的案例。第三,很难维护,因为这种转换的原因并不明显,很容易被重构掉。

正确的解决方案是确保您拥有一个完全基于您想要支持的最旧 JDK 版本的构建环境。然后二进制文件应该在以后的 JDK 上运行不变。例如,要使用 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

除了指定-source-target 选项之外,指定-bootclasspath 选项会将所有依赖项限制为在该位置找到的API,这当然必须与为其他选项指定的版本相匹配。这将防止对 JDK 8 的任何无意依赖进入生成的二进制文件。

在 JDK 9 及更高版本中,添加了一个新选项 --release。这有效地将源、目标和引导类路径一次性设置为相同的 JDK 平台级别。例如:

/path/to/jdk9/bin/javac --release 7 MyClass.java

使用--release 选项时,不再需要使用旧版JDK 的rt.jar 来进行构建,因为javac 包含早期版本的公共API 表。

**

更一般地说,当 JDK 在新的 JDK 版本中引入协变覆盖时,往往会发生此类问题。这发生在具有各种 Buffer 子类的 JDK 9 中。例如,ByteBuffer.limit(int) 被覆盖,现在返回 ByteBuffer,而在 JDK 8 及更早版本中,此方法继承自 Buffer 并返回 Buffer。 (在此区域中添加了几个其他协变覆盖。)在 JDK 9 上仅使用 -source 8 -target 8 编译的系统将遇到与 NoSuchMethodError 完全相同的问题。解决方法是一样的:使用--release 8

【讨论】:

  • 所以如果我在 jdk 8 的这个类中不使用任何新东西,我可以使用 -target 1.7。这是真的吗?
  • @bou 严格来说是的,但实际上不可能避免 JDK 8 类库的某些部分泄漏到您构建的类中。这是一个很好的例子,因为 keySet()'s 返回类型更改为返回 Java 8 特定的类,基本上没有办法在不引入 Java 8 依赖的情况下调用 keySet()
  • 我们在使用带有 -source 1.6 -target 1.6 的 JDK 8 构建然后尝试使用 JRE 6 运行时也遇到了这个问题。我很惊讶 JDK 8 API 更改为 keySet() 这样的基本方法在 Java 8 中导致与早期 Java 版本的二进制不兼容。如果 Java 8 中的方法签名保持不变:Set<k> keySet()</k> 那么它可能是二进制兼容的。我们的解决方法是使用 entrySet() 代替 - 因为我们在遍历 Map 时删除了一些项目。
【解决方案2】:

另一个答案建议修改您的代码(使用 keys() 而不是 keySet()),以便您可以在 Java 8 上编译源代码并在 Java 7 上运行。我认为这是一个倒退。

改为:

  • 如果您的目标是创建将在 Java 6、7 和 8 上运行的软件生产版本,那么最好的选择是在 JDK 6 上进行生产构建。

  • 如果您的目标是在 Java 8 上进行开发构建(但目前在源代码级别保持向后兼容),那么更改 animal-sniffer 的 maven 插件配置以忽略这些类;请参阅http://mojo.codehaus.org/animal-sniffer-maven-plugin/examples/checking-signatures.html 了解说明。

    但是,动物嗅探器可能会忽略太多;例如如果您在ConcurrentHashMap 中使用 Java 8 方法,它不会告诉您。您需要考虑这种可能性....

  • 如果您的目标是迁移到 Java 8(以便您可以开始在代码中使用新的 Java 8 功能),那么就去做吧。你的代码不会向后兼容,但你不能永远支持旧版本的 Java ...

(如果您考虑全局,这些建议并不相互排斥。)

【讨论】:

  • 我想构建所有版本,我认为最好的答案是你的第二个答案。我已经忽略了,谢谢。
  • 哪个答案建议在 8 上构建并在 7 上运行?我的?
  • @StuartMarks - 我编写了修改代码,以便您可以在 Java 8 上构建并在 Java 7 上运行。您是否建议这样做?我不这么认为!
  • 很公平,我看到的另一个答案建议修改代码,但没有具体讨论 7 与 8,所以我不确定。
  • +1 在 JDK 6 上构建以便在 6、7 和 8 上运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-02
  • 2017-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-10
  • 1970-01-01
相关资源
最近更新 更多