Question on returning instances of a (not exported) derived class

Peter Levart peter.levart at gmail.com
Thu Aug 25 11:41:58 UTC 2016


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



More information about the jigsaw-dev mailing list