Hi Martin,

Let me try to explain why...

On 08/25/2016 08:56 AM, Martin Lehmann wrote:
package pkgmain;

import pkgx.*;

public class Main {
   public static void main(String[] args) {
     DataFactory myDataFactory = new DataFactory();

// all OK
    System.out.println("Factory.createData(): " +
          myDataFactory.createData().getName());
    System.out.println("Factory.createInternalData1().toString(): " +
          myDataFactory.createInternalData1().toString());
    System.out.println("Factory.createInternalData1(): " +
          myDataFactory.createInternalData1().getName());

...these, I hope, are understandable. You are invoking the methods upon the exported type.


// [1]*does*  compile though return type of
// 'DataFactory.createInternalData2()' is not visible here
    System.out.println("Factory.createInternalData2(): " +
          myDataFactory.createInternalData2());

"Factory.createInternalData2(): " +
         myDataFactory.createInternalData2()

...is (or was, until replaced with an invokedynamic which should behave the same) equivalent to calling:

new StringBuilder().append("Factory.createInternalData2(): ").append(myDataFactory.createInternalData2()).toString()

...the 2nd append is StringBuilder::append(Object) method. Compiler knows that InternalData type will not be needed to invoke that method at runtime, so it doesn't require you to have access to that type at compile time either.



// [2] does not compile. error: toString() in Object is
//     defined in an inaccessible class or interface
// surprise! why is [1] OK while adding toString() is not?
    System.out.println("Factory.createInternalData2().toString(): " +
          myDataFactory.createInternalData2().toString());

I think this is to restrictive. If the compiler succeeded in compiling this code, It would compile the toString() invocation as:

invokevirtual #4 // Method java/lang/Object.toString:()Ljava/lang/String;

...which doesn't need to access InternalData type at runtime. So the compile time error should not be there IMO.

Are you sure you didn't have the toString() method overridden in InternalData at time of compilation?

Maybe the compiler is intentionally over restrictive here so that this type of code change (adding overriding public method to the concealed class) can not break the compilation? The error message is confusing, at least: "toString() in Object is defined in an inaccessible class or interface". How can Object be inaccessible?


// [3] does not compile. error: getName() in InternalData is
//     defined in an inaccessible class or interface
// suprise! getName() is overloaded, available in exported class Data

    System.out.println("Factory.createInternalData2().getName(): " +
        myDataFactory.createInternalData2().getName());
   }
}


This would compile down to:

invokevirtual #X // Method pkginternal/InternalData.toString:()Ljava/lang/String;

...because the compiler notices the static type of the .toString() target is InternalData which declares the toString() method (and overrides Object::toString method). So InternalData should be accessible at runtime for this invocation to succeed and at compile time for compilation to succeed. Note that the following should compile though (and run too):

    ((Data) myDataFactory.createInternalData2()).getName()



Regards, Peter

Reply via email to