JDK-8073620 : Lambda expression causes "ClassFormatError: Duplicate field name&signature"
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 8u31
  • Priority: P4
  • Status: Closed
  • Resolution: Cannot Reproduce
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2015-02-09
  • Updated: 2015-05-21
  • Resolved: 2015-02-23
Related Reports
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

EXTRA RELEVANT SYSTEM CONFIGURATION :
This is the output generated in NetBeans using Help=> About:

Product Version: NetBeans IDE 8.0.2 (Build 201411181905)
Java: 1.8.0_31; Java HotSpot(TM) 64-Bit Server VM 25.31-b07
Runtime: Java(TM) SE Runtime Environment 1.8.0_31-b13
System: Windows 7 version 6.1 running on amd64; Cp1252; en_US (nb)
User directory: C:\Users\JohnDoe\AppData\Roaming\NetBeans\8.0.2
Cache directory: C:\Users\JohnDoe\AppData\Local\NetBeans\Cache\8.0.2

A DESCRIPTION OF THE PROBLEM :
This is a simple Java 8 application which does the following:
- Declare a "Widget" class.
- Declare a "WidgetCollection" class that implements Iterable to contain Widgets.
- WidgetCollection overrides iterator() to support forEach().
- Populate the WidgetCollection with three Widget instances.
- Iterate through the collection, printing the names of the instances to the console.

If the iterator in WidgetCollection is instantiated using an anonymous class there is no problem. However, if the iterator is instantiated using a lambda expression which declares a local variable then a ClassFormatError occurs in the following scenario: 
[1] The local variable is named after one of the locally overriden methods (i.e. "next" or "hasNext").
[2] The local variable is of type Widget.
[3] The lambda expression is actually used at runtime.
The ClassFormatError only occurs when all three of those conditions are met.

I previously incorrectly raised this as a NetBeans issue 247843 (https://netbeans.org/bugzilla/show_bug.cgi?id=247843).

ADDITIONAL REGRESSION INFORMATION: 
Not applicable - this issue relates to using a lambda expression in release 1.8.0_31.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See the declaration on line 16: private Widget harmless = null;

Simply change the name of the variable from "harmless" to "next" or "hasNext" and then rerun the application to cause the ClassFormatError.

Note that "next" or "hasNext" are also the names of local @Override methods.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
.foreach() call: W1 W2 W3 
ACTUAL -
A ClassFormatError occurs when running the application:

Exception in thread "main" java.lang.ClassFormatError: Duplicate field name&signature in class file createbadclassfile/WidgetCollection$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 createbadclassfile.WidgetCollection.lambda$new$0(CreateBadClassFile.java:15)
	at createbadclassfile.WidgetCollection$$Lambda$1/1554547125.iterator(Unknown Source)
	at createbadclassfile.WidgetCollection.iterator(CreateBadClassFile.java:39)
	at java.lang.Iterable.forEach(Iterable.java:74)
	at createbadclassfile.CreateBadClassFile.main(CreateBadClassFile.java:50)
.foreach() call: Java Result: 1


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package createbadclassfile;
import java.util.Iterator;
import java.util.LinkedList;

class Widget {
    private String name;
    Widget(String s) { name = s; }
    @Override public String toString() { return name; }
}

class WidgetCollection<Widget> implements Iterable<Widget> {
    private LinkedList<Widget> data;
    // Instantiate an Iterable instance using a Lambda expression.
    // Causes ClassFormatError if a local variable of type Widget is named after one of the methods.
    private final Iterable<Widget> myIterator1 = () -> new Iterator<Widget>() {
        private Widget harmless = null; // Rename to "hasNext" or "next" to cause ClassFormatError
        private int index = 0;
        @Override public boolean hasNext() { return index < data.size(); }
        @Override public Widget next() { return data.get(index++); }
    };

    // Instantiate an Iterable instance using an anonymous class.
    // Always works fine regardless of the name of the local variable.
    private final Iterable<Widget> myIterator2 = 
        new Iterable<Widget>() {
        @Override
        public Iterator<Widget> iterator() {
            return new Iterator<Widget>() {
                private Widget hasNext = null;
                private int index = 0;
                @Override public boolean hasNext() { return index < data.size(); }
                @Override public Widget next() { return data.get(index++); }
            };
        }
    };
    public WidgetCollection() { data = new LinkedList<>(); }
    public void add(Widget e) { data.add(e); }
    @Override public String toString() { return data.toString(); }
    // Change the next line to use "myIterator2" to use an anonymous class instead 
    // of a lambda expression when instantiating the Iterable instance.
    @Override public Iterator<Widget> iterator() { return myIterator1.iterator(); } 
}

public class CreateBadClassFile {

    public static void main(String[] args) {
        WidgetCollection<Widget> widgets = new WidgetCollection<>();
        widgets.add(new Widget("W1"));
        widgets.add(new Widget("W2"));
        widgets.add(new Widget("W3"));
        System.out.print(".foreach() call: ");
        widgets.forEach(w -> System.out.print(w + " "));
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Change the name of the local variable as detailed in the "Steps to Reproduce" section above.


Comments
Checked this for JDK 8u25, 8u31, 8u40 ea and could never reproduce this issue. Note: Tested from command line as well NetBeans. Therefore, closing this as cannot reproduce.
23-02-2015