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