-
Notifications
You must be signed in to change notification settings - Fork 22
Description
reproduction steps
using Scala 2.13.5, on Oracle Java SE Development Kit 8u202.
I tried overriding a method in a Java class.
// javapkg/JavaSuper.java
package javapkg;
public class JavaSuper {
public void fn() { System.out.println("JavaSuper#fn()"); }
}// pkg/ScalaSub.scala
package pkg
import javapkg.JavaSuper
object Cls {
class ScalaSub extends JavaSuper {
override def fn(): Unit = println("ScalaSub#fn()")
}
}I changed each other's access modifiers as shown in the table below and verified.
It summarizes whether it can be compiled and whether it is the expected result.
Rules for overriding from Java.
Modifier(JavaSuper) |
Modifier(ScalaSub) |
Compile | Expected |
|---|---|---|---|
public |
none(public) |
○ | yes |
public |
protected |
○ | no |
public |
private |
× | yes |
public |
protected[Cls] |
○ | no |
public |
private[Cls] |
○ | no |
public |
protected[pkg] |
○ | no |
public |
private[pkg] |
○ | no |
public |
protected[this] |
○ | no |
public |
private[this] |
○ | no |
Java's protected |
none(public) |
○ | yes |
Java's protected |
protected |
○ | no |
Java's protected |
private |
× | yes |
Java's protected |
protected[Cls] |
○ | no |
Java's protected |
private[Cls] |
× | yes |
Java's protected |
protected[pkg] |
○ | no |
Java's protected |
private[pkg] |
× | yes |
Java's protected |
protected[this] |
○ | no |
Java's protected |
private[this] |
○ | no |
| package access | none(public) |
○ | yes |
| package access | protected |
○ | no |
| package access | private |
× | yes |
| package access | protected[Cls] |
○ | no |
| package access | private[Cls] |
○ | no |
| package access | protected[pkg] |
○ | yes |
| package access | private[pkg] |
○ | yes |
| package access | protected[this] |
○ | no |
| package access | private[this] |
○ | no |
private |
none(public) |
× | yes |
private |
protected |
× | yes |
private |
private |
× | yes |
private |
protected[Cls] |
× | yes |
private |
private[Cls] |
× | yes |
private |
protected[pkg] |
× | yes |
private |
private[pkg] |
× | yes |
private |
protected[this] |
× | yes |
private |
private[this] |
× | yes |
problem
-
Subclasses have stricter access privileges.
Cases
Modifier( JavaSuper)Modifier( ScalaSub)publicprotectedpublicprotected[Cls]publicprivate[Cls]publicprotected[pkg]publicprivate[pkg] -
It becomes inaccessible from directly under the package to which the class
JavaSuper.Cases
Modifier( JavaSuper)Modifier( ScalaSub)Java's protectedprotectedJava's protectedprotected[Cls]package access protectedpackage access protected[Cls]package access private[Cls] -
If
pkgor its subpackages are unrelated to thejavapkg, they will be inaccessible fromjavapkg.Cases
Modifier( JavaSuper)Modifier( ScalaSub)Java's protectedprotected[pkg] -
Access modifiers on the subclass side do not work.
Cases
Modifier( JavaSuper)Modifier( ScalaSub)publicprotected[this]Java's protectedprotected[this]package access protected[this]※Treated the same as the modifier on the
JavaSuperside. (Very confusing!) -
Calling via
ScalaSubthrowjava.lang.IllegalAccessError, calling viaJavaSupercallsfn()on theJavaSuperside.Cases
As reported in private[this] subverts override accessibility check #11913.
Modifier( JavaSuper)Modifier( ScalaSub)publicprivate[this]Java's protectedprivate[this]package access private[this]
5. is not mentioned here because the issue already exists.
2. and 3. may be unavoidable due to Java package access specifications, so I'm not going to go into it.
I think problem is 1. and 4..
I didn't expect that public methods could be overridden with stricter modifiers or protected[this] wouldn't work.
Please let me know if these are specifications.
// javapkg/JavaSuper.java
package javapkg;
public class JavaSuper {
public void fn1() { System.out.println("JavaSuper#fn1()"); }
public void fn2() { System.out.println("JavaSuper#fn2()"); }
}// pkg/ScalaSub.scala
package pkg
import javapkg.JavaSuper
object Cls {
class ScalaSub extends JavaSuper {
protected override def fn1(): Unit = println("ScalaSub#fn1()") // 1. OK but strange, access only from subclasses via ScalaSub.
protected[this] override def fn2(): Unit = println("ScalaSub#fn2()") // 4. This is even more strange, can be accessed anywhere via ScalaSub.
}
}import javapkg.JavaSuper
import pkg.Cls.ScalaSub
val j: JavaSuper = new ScalaSub()
val s: ScalaSub = new ScalaSub()
scala> j.fn1()
ScalaSub#fn1()
scala> j.fn2()
ScalaSub#fn2()
scala> s.fn1() // It's unfamiliar to be inaccessible only from subclasses.
^
error: method fn1 in class ScalaSub cannot be accessed as a member of pkg.Cls.ScalaSub from class $iw
Access to protected method fn1 not permitted because
enclosing class $iw is not a subclass of
class ScalaSub in object Cls where target is defined
scala> s.fn2() // `protected[this]` seems to have been ignored.
ScalaSub#fn2()
Supplement
1. is only occurs if you override a method defined in a Java classes in Scala 2.8.2 or later.
4. is even before Scala 2.8.1, it seems that protected can be overridden with protected[this], allowing access via another instance.
I think the problem became more noticeable as we were able to override public etc, in 2.8.2.
By the way, in Scala 3.0.0-RC1, an error occurred when the JavaSuper side was public and package access, and the result was as expected.
(Java's protected is still suspicious even in Scala 3... But that should be mentioned in Dotty.)