Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot refer to protected inner class in Scala when inheriting from Java (with byte code only) #4737

Closed
scabug opened this issue Jun 25, 2011 · 6 comments
Assignees

Comments

@scabug
Copy link

scabug commented Jun 25, 2011

I am writing a Scala class to inherit from a Java class, and I must override a method that takes a protected Java inner class as a parameter. The Java dependency comes as a jar without source code.

I have the exact same setup as found in #3120 except that I do not have the Java source code available, so scalac only knows about the Java dependency by looking at the byte code (in jar or class files).

This is how I replicate the problem in a simple setting without my jar dependency:

// javapkg/JavaSuperClass.java
package javapkg;
public class JavaSuperClass {
    protected class JavaInnerClass {
    }
    public void method(JavaInnerClass javaInnerclass) {
        System.out.println("hello");
    }
}
// scalapkg/ScalaSubClass.scala
package scalapkg
import javapkg.JavaSuperClass
class ScalaSubClass extends JavaSuperClass {
  override def method(javaInnerClass: JavaSuperClass#JavaInnerClass) {
    println("world")
  }
}

This is what happens:

$ cd javapkg
$ javac JavaSuperClass.java
$ cd ../scalapkg
$ scalac -cp .. ScalaSubClass.scala
ScalaSubClass.scala:6: error: class JavaInnerClass in class JavaSuperClass cannot be accessed in javapkg.JavaSuperClass
 Access to protected class JavaInnerClass not permitted because
 prefix type javapkg.JavaSuperClass does not conform to
 class ScalaSubClass in package scalapkg where the access take place
  override def method(javaInnerclass: JavaSuperClass#JavaInnerClass) {
                                                     ^
one error found

Note, if I change JavaSuperClass#JavaInnerClass to simply JavaInnerClass, I get this:

ScalaSubClass.scala:6: error: method method overrides nothing
  override def method(javaInnerClass: JavaInnerClass) {
               ^
one error found

I know this sounds very similar to #1806, but I believe this is unrelated because there are no statics anywhere in my example.

I feel like something is wrong, because when I put the same code into a mixed java/scala project in Eclipse, it seemed to compile fine (with the latter JavaInnerClass syntax); it's only when I compile the Scala code with only the Java byte code (and no Java source code) that I cannot get it to work. This should also be able to work as well if method is protected in both classes.

I posted this to StackOverflow here, but was told it was probably a compiler bug as opposed to incorrect usage.

@scabug
Copy link
Author

scabug commented Jun 25, 2011

Imported From: https://issues.scala-lang.org/browse/SI-4737?orig=1
Reporter: Mike Mintz (mikemintz)
Affected Versions: 2.9.0

@scabug
Copy link
Author

scabug commented Jul 5, 2011

@paulp said:
Among the many problems we find here is that making J public changes the behavior: in that case compiling against source or bytecode it is the same, it rejects it either way.

% scalac3 s/s.scala j/J.java 
s/s.scala:6: error: class JavaInnerClass in class J cannot be accessed in j.J
 Access to protected class JavaInnerClass not permitted because
 prefix type j.J does not conform to
 class ScalaSubClass in package s where the access take place
  override def method(javaInnerClass: J#JavaInnerClass) {
                                        ^
one error found

So in other words, in order for ScalaSubClass to refer to J#JavaInnerClass, the access has to be made MORE restrictive. So that's obviously a bug too.

The only thing that makes sense from my point of view is to allow an access-restricted java-defined inner class to be referenced from any subclass or other class with access if the given prefix is the java class inside which it was defined. Otherwise you can easily run into situations where you e.g. can't override a method because you can't refer to the parameter type, as shown here.

I'm not sure what good it does to have vigilant enforcement of access restrictions on when and how you refer, just refer, to types. There are other variations on the theme where you can't override something because you're not allowed to express the type of the thing you're overriding, even though you have access due to subclassing. Or you can override it, but because you aren't allowed to say the type, you can't preserve the existing access restrictions; or, you are forced to let the expression type be inferred when you wanted to express something less precise but couldn't.

See also: #1352, #1994, #1800, #3272, #1469.

@scabug
Copy link
Author

scabug commented Jul 5, 2011

Mike Mintz (mikemintz) said:
Just to be clear, I believe the example I ultimately want to work that compiles properly against source but not against bytecode is the second one:

override def method(javaInnerClass: JavaInnerClass) {

@scabug
Copy link
Author

scabug commented Jul 5, 2011

@paulp said:
I know. That's why the error message in my comment is directly from it.

@scabug
Copy link
Author

scabug commented Jul 6, 2011

@adriaanm said:
(fixed in r25240 by odersky) Changes semantics so that protected access rule (selector must be subclass of current class) does not hold for type members. Fixes t4737. Review by extempore. Spec change in a seperate commit.

@scabug scabug closed this as completed Jul 6, 2011
@scabug
Copy link
Author

scabug commented Jul 6, 2011

Commit Message Bot (anonymous) said:
(extempore in r25244) Test case for #4737, no review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants