United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6657499 javac 1.6.0 fails to compile class with inner class
JDK-6657499 : javac 1.6.0 fails to compile class with inner class

Details
Type:
Bug
Submit Date:
2008-01-31
Status:
Closed
Updated Date:
2011-05-18
Project Name:
JDK
Resolved Date:
2011-05-18
Component:
tools
OS:
linux
Sub-Component:
javac
CPU:
x86
Priority:
P3
Resolution:
Fixed
Affected Versions:
6
Fixed Versions:

Related Reports
Backport:
Relates:
Relates:

Sub Tasks

Description
FULL PRODUCT VERSION :
[cps@loddont genericsTest]$ java -version
java version "1.5.0_13"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13-b05)
Java HotSpot(TM) Server VM (build 1.5.0_13-b05, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Linux loddont 2.6.22.9-61.fc6PAE #1 SMP Thu Sep 27 18:27:50 EDT 2007 i686 i686 i386 GNU/Linux


A DESCRIPTION OF THE PROBLEM :
This appears to occur when you define a class inside a method and there are generic types in the outer class.  It appears that they do not pass to the nested class (sorry if thats the wrong terminology for a named class in a method).  I believe this is a bug because it used to compile and it seems inconsistent.  You can minimally reproduce this by trying to compile this lot:-

public interface Foo<T> {
  T foo();
}

public class Bar<T> implements Foo<T> {
  public T foo() {
    class FooImpl implements Foo<T> {
      public T foo() {
        return null;
      }
    }
    return new FooImpl().foo();
  }
}

You get this error with javac/1.6.0_4

[cps@loddont genericsTest]$ /usr/java/jdk1.6.0_04/bin/javac -d classes src/*
src/Bar.java:9: incompatible types
found   : java.lang.Object
required: T
    return new FooImpl().foo();
                            ^
1 error

However, as indicated, java 1.5.0 is quite happy:-

[cps@loddont genericsTest]$ /usr/java/jdk1.5.0/bin/javac -d classes src/*
[cps@loddont genericsTest]$

It seems unlikely that this is the correct behaviour since if the type T is not available in the nested class, then I'd exect the line

    class FooImpl implements Foo<T> {

to fail to compile because T isn't defined.

I should mention that the eclipse compiler is quite happy with this construct.  Maybe they're wrong but I would still suggest that the javac compiler doesn't make much sense here as the generic type T is clearly visible in some sense.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1) Create src dir and classes dir.
2) Put 'foo.java' and 'bar.java' in src dir.
3) javac -d classes src/*


REPRODUCIBILITY :
This bug can be reproduced always.

CUSTOMER SUBMITTED WORKAROUND :
The following modified version of Bar does compile with 1.6.0_4 and 1.5.0 oddly enough

public class Bar<T> implements Foo<T> {
  public T foo() {
    class FooImpl<T2 extends T> implements Foo<T> {
      public T foo() {
        return null;
      }
    }
    return new FooImpl<T>().foo();
  }
}

as does the slightly more sensible version

public class Bar<T> implements Foo<T> {
  public T foo() {
    class FooImpl<T2> implements Foo<T2> {
      public T2 foo() {
        return null;
      }
    }
    return new FooImpl<T>().foo();
  }
}

So you can always make the class external and/or add parameterised types.  I was only doing this because the class concerned was never likely to be of use outside the method so it seemed the best place for it.

                                    

Comments
EVALUATION

This bug is due to the interplay between resolution of types and local inner classes and not to generics as it may seem at first. When the type FooImpl is accessed within the body of foo(), javac looks for a suitable enclosing instance from which FooImpl can be accessed. However, since FooImpl is a local inner classes (which means that the owner of FooImpl is a method and not a class), the process of finding such an instance fails. This, in turn, trigger the erasure of the symbol to be resolved, causing all type variable to be replaced by their declared bounds. This explains why javac complains about finding a T instead of an Object (the declared bound of T - the signature of foo() has already been erased!). 

The problem has been introduced when fixing CR 5009484 in Mustang. A segment of code in Attr.java was added in order to access a given symbol through the most appropriate enclosing instance (the one in which the symbol is accessible, as not accessible symbols might hide accessible ones). However there's no point for performing an enclosing instance lookup when attributing a *TYPE*, since types are always acessible as toplevel types (inner classes get translated to toplevel classes) and no hiding occurs between class names (it's not possible for an inner class to have the same name as one of its enclosing classes).

The solution is in preventing the enclosing instance lookup when attributing a type.
                                     
2008-02-04
SUGGESTED FIX

see http://sa.sfbay.sun.com/projects/langtools_data/7/6657499/
                                     
2008-02-04



Hardware and Software, Engineered to Work Together