java 8中的ClassFormatError?

我正在创建一个类似于java.util.LinkedList的类,并且得到了一个完全出乎意料的ClassFormatError 。 我的IDE没有显示任何警告。 仅供参考,我使用的是Java 8u20。 更新:在Java 8u60中修复。

T̶h̶i̶s̶̶i̶n̶c̶l̶u̶d̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶

 import java.io.Serializable; import java.util.*; import java.util.function.Function; public class Foo implements Deque, Serializable { private static final long serialVersionUID = 0L; private final Node sentinel = sentinelInit(); private final Iterable nodes = (Iterable & Serializable) () -> new Iterator() { @SuppressWarnings("UnusedDeclaration") private static final long serialVersionUID = 0L; private Node next = sentinel.next; @Override public boolean hasNext() { return next != sentinel; } @Override public Node next() { if (!hasNext()) { throw new NoSuchElementException(); } Node old = next; next = next.next; return old; } @Override public void remove() { if (next.previous == sentinel) { throw new IllegalStateException(); } removeNode(next.previous); } }; @Override public boolean isEmpty() { return false; } @Override public Object[] toArray() { return new Object[0]; } @Override public  T[] toArray(T[] a) { return null; } @Override public boolean containsAll(Collection c) { return false; } @Override public boolean removeAll(Collection c) { return false; } @Override public boolean retainAll(Collection c) { return false; } @Override public void clear() { } @Override public void addFirst(E e) { } @Override public void addLast(E e) { } @Override public boolean offerLast(E e) { return false; } @Override public E removeFirst() { return null; } @Override public E removeLast() { return null; } @Override public E pollFirst() { return null; } @Override public E getFirst() { return null; } @Override public E getLast() { return null; } @Override public E peekFirst() { return null; } @Override public boolean removeFirstOccurrence(Object o) { return false; } @Override public boolean removeLastOccurrence(Object o) { return false; } @Override public E remove() { return null; } @Override public E element() { return null; } @Override public void push(E e) { } @Override public E pop() { return null; } @Override public boolean contains(Object o) { return false; } @Override public boolean offerFirst(E e) { return false; } @Override public E pollLast() { return null; } @Override public E peekLast() { return null; } @Override public boolean offer(E e) { Node node = new Node(e); sentinel.previous.next = node; node.previous = sentinel.previous; sentinel.previous = node; node.next = sentinel; return true; } @Override public E poll() { return null; } @Override public E peek() { return null; } @Override public boolean remove(Object o) { for (Node node : nodes) { if (node.value.equals(o)) { removeNode(node); return true; } } return false; } @Override public int size() { return 0; } @Override public Iterator descendingIterator() { return null; } @Override public Iterator iterator() { return new Iterator() { private final Iterator backingIter = nodes.iterator(); @Override public boolean hasNext() { return backingIter.hasNext(); } @Override public E next() { return backingIter.next().value; } @Override public void remove() { backingIter.remove(); } }; } private Node sentinelInit() { Node sentinel = new Node(); sentinel.next = sentinel; sentinel.previous = sentinel; return sentinel; } private void removeNode(Node node) { node.previous.next = node.next; node.next.previous = node.previous; } private class Node implements Serializable { private static final long serialVersionUID = 0L; public E value; public Node next; public Node previous; public Node(E value) { this.value = value; } public Node() { this(null); } } public static  List map(Function function, Iterable objects) { ArrayList returned = new ArrayList(); for (I obj : objects) { returned.add(function.apply(obj)); } return returned; } @Override public boolean addAll(Collection c) { boolean ret = false; for (boolean changed : map(this::add, c)) { if (changed) { ret = true; } } return ret; } @Override public boolean add(E e) { if (!offer(e)) { throw new IllegalStateException(); } return true; } public static void main(String[] args) { Foo list = new Foo(); System.out.println("Constructed list"); list.addAll(Arrays.asList("a", "B", "c")); System.out.println("Added a, B and c."); list.forEach(System.out::println); list.remove("B"); list.forEach(System.out::println); } } 

这是输出:

 Constructed list Added a, B and c. Exception in thread "main" java.lang.ClassFormatError: Duplicate field name&signature in class file uk/org/me/Foo$1 at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:760) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at java.net.URLClassLoader.defineClass(URLClassLoader.java:455) at java.net.URLClassLoader.access$100(URLClassLoader.java:73) at java.net.URLClassLoader$1.run(URLClassLoader.java:367) at java.net.URLClassLoader$1.run(URLClassLoader.java:361) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:360) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at uk.org.me.Foo.lambda$new$c83cc381$1(Foo.java:18) at uk.org.me.Foo$$Lambda$1/1392838282.iterator(Unknown Source) at uk.org.me.Foo$2.(Foo.java:222) at uk.org.me.Foo.iterator(Foo.java:221) at java.lang.Iterable.forEach(Iterable.java:74) at uk.org.me.Foo.main(Foo.java:300) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134) 

您的代码有效, ClassFormatError是由Java 8编译器中的错误引起的。 我认为你已经发现了一个边缘情况:加载为lambda表达式体生成的类文件,该体表包含一个与该体内正在覆盖的方法同名的变量可能会失败。

我将您的代码粘贴到最新版本的Intellij Idea(13.1.5),NetBeans(8.0.1)和Eclipse(Kepler SR2)中。 在所有情况下编译的代码。 运行时,它在NetBeans和Idea中的ClassFormatError失败了,但它在Eclipse中运行良好。

堆栈跟踪显示错误是由于尝试加载由分配给实例变量Foo.nodes的lamba表达式生成的类失败而引起的。 根本原因是在分配给Foo.nodes的lambda体内,您有一个与overriden方法next()同名的变量。

正如您已经注意到的,如果您在声明private Node next = sentinel.next;重命名变量private Node next = sentinel.next; 一些独特的价值(例如next2 )问题消失了。 同样,将其重命名为不同的重写方法(即removehasNext )会导致问题再次出现。 伴随ClassFormatError (“重复字段名称和签名”)的消息准确地描述了问题,但没有解释为什么这应该是这样一个精确定义的运行时错误; 编译的代码,并且方法和变量具有相同的名称是合法的(如果不明智)。 用“this”前缀下一个。 没有解决问题。

在失败的Idea类文件上运行javap(在我的情况下foo/Foo$1 )显示:

 C:\Idea\Java8\Foo\out\production\Foo\foo>"C:\Program Files\Java\jdk1.8.0_20\bin\javap.exe" Foo$1.class Compiled from "Foo.java" class foo.Foo$1 implements java.util.Iterator.Node> { final foo.Foo this$0; foo.Foo$1(foo.Foo); public boolean hasNext(); public foo.Foo.Node next(); public void remove(); } 

但是,在运行正常的相应Eclipse类上运行javap会显示一个额外的条目:

 public java.lang.Object next(); 

如果lambda主体中的next变量被重命名为唯一的,允许代码运行,那么额外的方法条目也神奇地出现在Idea类文件中。 我建议您将此报告为JetBrains的编译器错误。

另外,作为一个不相关的问题, Iterator接口在Java 8中将remove()实现为默认方法。由于您从不调用Iterator.remove()您可以删除两个实现。

更新 :我针对Oracle的这个问题提出了一个错误( JDK-8080842 ),并且在1.8.0_60版本中将对它进行修复。

更新到更新(2015年8月27日) :Oracle在8u60之后的所有版本中修复了此问题。 我validation了对8u60的修复。 Bug JDK-8080842是指。

可迭代nodes内的两个变量和next调用的private class Node属于同一类型( Node )并且具有相同的名称:在该私有类中,它们都在范围内,但不能消除歧义。

因此,问题不是 next字段和next()方法,而是Iterable nodesprivate class Nodenext命名变量。

如果您将sentinel.next重构为其他名称:

 private final Iterable nodes = (Iterable & Serializable) () -> new Iterator() { @SuppressWarnings("UnusedDeclaration") private static final long serialVersionUID = 0L; private Node snext = sentinel.next; // refactor `next` to `snext` ... 

它将编译并运行。

作为个人笔记,我不鼓励你编写这样的代码:阅读并弄清楚它的作用是非常困难的。

希望这可以帮助!

尝试使用jdk安装在命令行上编译’.java’文件。

使用JDK编译后,看看是否可以运行新的“.class”文件。 如果您的类加载器在这些条件下工作,则IDE不使用javac的JDK副本(或者配置为编译到不同的–target级别)。

Eclipse过去常常发布它自己的编译器(我相信它仍然存在)。 Netbeans始终遵循JDK编译器。 在过去的几个版本中,JDK编译器在IDE集成方面进行了相当多的工作。 但是,我不认为Eclipse已经重新设计了他们的IDE以利用这一努力。

无论如何,您很快就能知道您的问题是JDK编译器还是IDE编译器。