javagenericscompiler-errors

java compiler oddity: field declared in same class, yet "not visible"


The eclipse compiler refuses to compile the following code, stating that the field s is not visible. (IBM's Aspect J compiler also refuses, stating that "s could not be resolved") Why is that?

public class Test {

    String s;

    void foo(Object o) {
        String os = getClass().cast(o).s;
    }
}

The Java Language Specification states:

Otherwise, we say there is default access, which is permitted only when the access occurs from within the package in which the type is declared.

The way I understand it, the field is declared and accessed in the same compilation unit, thus within the same package, and should therefore be accessible.

Even more strangely, adding a downcast from ? extends Test to Test makes the field visible, i.e. the following code compiles:

public class Test {

    String s;

    void foo(Object o) {
        Test t = getClass().cast(o);
        String os = t.s;
    }
}

Have I stumbled across a compiler bug, or misunderstood the Java Spec?

Edit: I am on another computer now. Here, javac accepts the code, but eclipse still doesn't. Versions on this machine:

Eclipse Platform

Version: 3.4.2 Build id: M20090211-1700

JDK 1.6.0

Edit 2 Indeed, javac accepts the code. I had tested by running the ant build, which uses IBM's Ascpect J compiler ...


Solution

  • Try this:

    void foo(Object o) {
        Test foo = getClass().cast(o);
        String so = foo.s;
    }
    

    [Edit to clarify]:

    getClass().cast(o) returns an object of type 'capture#1-of? extends Test' and not Test. So the issue is related to generics and how the compiler treats it. I don't know the details of the spec on generics but given that some compilers (per comments here) do accept your code, then this is either a loop hole in the spec or some of these compilers are not entirely according to spec.

    [Last thoughts]: I believe the eclipse compiler is actually (carefully) correct here. The object o may in fact be an extension of Test (and defined in another package) and the compiler has no way of knowing if that is indeed the case or not. So it is treating it as the worst case of an instance of an extension defined in another package. It would have been super correct if adding a final qualifier to class Test would have allowed access to field s, but it does not.