Skip to content

Nested Java interfaces not found in Scala and Java #1409

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

Closed
scabug opened this issue Oct 9, 2008 · 16 comments
Closed

Nested Java interfaces not found in Scala and Java #1409

scabug opened this issue Oct 9, 2008 · 16 comments

Comments

@scabug
Copy link

scabug commented Oct 9, 2008

Compiling the following two .java files with javac succeeds as expected,

OuterInterface.java:

public interface OuterInterface {
  public interface InnerInterface {
    public void foo();
  }
}
AbstractImpl.java:

public abstract class AbstractImpl implements OuterInterface {
  public abstract InnerInterface create();
}

However, if the following .scala class is added,

ConcreteImpl.scala:

class ConcreteImpl extends AbstractImpl {
  def create : InnerInterface = null
}

and all three are compiled with scalac 2.7.2.RC2, the following unexpected errors are reported against both the Scala and the Java,

miles@frege:test$$ scalac *
ConcreteImpl.scala:2: error: not found: type InnerInterface
  def create : InnerInterface = null
               ^
AbstractImpl.java:2: error: not found: type InnerInterface
  public abstract InnerInterface create();
                  ^
two errors found

The only workaround is to add redundant qualifiers to both the Java and the Scala sources,

AbstractImpl2.java:

public abstract class AbstractImpl implements OuterInterface {
  public abstract OuterInterface.InnerInterface create();
}
ConcreteImpl2.scala:

class ConcreteImpl extends AbstractImpl {
  def create : OuterInterface.InnerInterface = null
}
@scabug
Copy link
Author

scabug commented Oct 9, 2008

Imported From: https://issues.scala-lang.org/browse/SI-1409?orig=1
Reporter: @milessabin

@scabug
Copy link
Author

scabug commented Oct 9, 2008

@odersky said:

That's unfortunately not an easy one (I am not even sure there is a way to fix this). The problem is that in

public interface OuterInterface {
  public interface InnerInterface {
    public void foo();
  }
}

InnerInterface is a static member of OuterInterface. So it can't be inherited and the error in ConcreteImpl.scala is as expected. As to AbstractImpl.java, the Java parser is just a parser, it can't have any context information. The only thing one could possibly consider is import all companion objects of inherited classes and interfaces. I.e AbstractImpl could be translated to the following Scala code:

  abstract class AbstractImpl extends _root_.java.lang.Object with OuterInterface {
    import OuterInterface._
    def create(): InnerInterface
  }

But that's both incomplete and possibly incorrect. It's incomplete because
interfaces inherited by OuterInterface are not considered. It's possibly incorrect
because a companion object might not exist. Neither of the two shortcomings can be solved without adding context information, which is beyond our design. So I am afraid it's a wontfix until some completely new design emerges.

@scabug
Copy link
Author

scabug commented Oct 9, 2008

@milessabin said:
There's no particular problem with the qualifier being required in the Scala source, however the error doesn't go away if you compile OuterInterface.java, AbstractImpl.java along with ConcreteImpl2.scala: scalac still reports an error against the Java.

The problem here is that it forces existing correct Java code to be modified before it can be used in a mixed Scala/Java project.

@scabug
Copy link
Author

scabug commented Oct 10, 2008

@milessabin said:
Fixed in r14226. Thanks.

@scabug
Copy link
Author

scabug commented Oct 10, 2008

@milessabin said:
Whoops, sorry that should have been r16224.

@scabug
Copy link
Author

scabug commented Dec 16, 2008

@milessabin said:
Reopening ... r16226 was only a partial fix.

@scabug
Copy link
Author

scabug commented May 21, 2010

@SethTisue said:
fwiw, this is still a problem as of 2.8.0.RC2. in my case I'm hitting it with an inner enum instead of an inner interface.

@scabug
Copy link
Author

scabug commented May 24, 2010

@hubertp said:
I am getting the same issue with nested static classes. This seems to be a problem for #3347.

@scabug
Copy link
Author

scabug commented Apr 2, 2011

@paulp said:
We have it reopened as only partially fixed and two voices of agreement that it's not fixed, but the only code sample in this ticket IS fixed. Could someone please update the ticket with something which doesn't work?

@scabug
Copy link
Author

scabug commented Apr 3, 2011

@SethTisue said:
oh man. this was hard to isolate.

full compilation, with all sources passed to scalac, succeeds. but then incremental recompilation, with only some sources passed to scalac, fails:

% cat I.java
interface I { enum E { E1 } ; }
% cat J.java
class J implements I { private E e = E.E1 ; }
% cat S.scala
class S extends J
% /usr/local/scala-2.9.0.RC1/bin/scalac I.java J.java S.scala
% javac I.java J.java
% rm J.class S.class
% /usr/local/scala-2.9.0.RC1/bin/scalac J.java S.scala
J.java:1: error: not found: type E
class J implements I { private E e = E.E1 ; }
                               ^
one error found

the problem goes away if E is fully qualified in J:

% cat I.java
interface I { enum E { E1 } ; }
% cat J.java
class J implements I { private I.E e = I.E.E1 ; }
% cat S.scala
class S extends J
% /usr/local/scala-2.9.0.RC1/bin/scalac I.java J.java S.scala
% javac I.java J.java
% rm J.class S.class
% /usr/local/scala-2.9.0.RC1/bin/scalac J.java S.scala

@scabug
Copy link
Author

scabug commented Apr 3, 2011

@SethTisue said:
P.S. this does actually happen with my real codebase, with sbt's incremental recompilation. sbt will do the above recompilation if it determines that J.java has been changed.

@scabug
Copy link
Author

scabug commented Apr 12, 2011

@hubertp said:
Related to #4402, also protobuf library generates code exactly like that and you typically have to manually fully qualify types.

@scabug
Copy link
Author

scabug commented Apr 12, 2011

@lrytz said:
Thanks for the code, Seth. I believe the bug is now in the classfile parser. It can be further reduced to the following:

lampmac3:sandbox luc$$ ls
total 24
-rw-r--r--  1 luc  staff  36 Apr 12 16:00 A.java
-rw-r--r--  1 luc  staff  25 Apr 12 16:13 B.java
-rw-r--r--  1 luc  staff  48 Apr 12 16:12 Test.scala
lampmac3:sandbox luc$$ cat A.java 
interface A {
    interface I { }
}
lampmac3:sandbox luc$$ cat B.java 
class B implements A { }
lampmac3:sandbox luc$$ cat Test.scala 
class Test extends B {
  def foo(): I = null
}

lampmac3:sandbox luc$$ javac *.java
lampmac3:sandbox luc$$ ../target/pack/bin/scalac Test.scala
Test.scala:2: error: not found: type I
  def foo(): I = null
             ^
one error found

@scabug
Copy link
Author

scabug commented Nov 10, 2011

@paulp said:
You can take B out of the equation: it's the same if Test extends A directly.


// together ok
% scalac3 Test.scala A.java 
// separate not ok
% scalac3 Test.scala 
Test.scala:2: error: not found: type I
  def foo(): I = null
             ^
one error found

Even if I define the java like this (public/public)

package j;

public interface A {
    public interface I { }
}

...why does scala see it as "protected type I = A.I" ?

@scabug
Copy link
Author

scabug commented May 21, 2012

Sasha Kazachonak (kazachonak) said:
Seems like this is related to the problem that prevents people from using Hadoop with Google App Engine in Scala.

You can look at simple code examples here:
https://groups.google.com/group/scala-user/browse_thread/thread/3a05e804dd0f48e3

Depending on whether you compile scala+java sources together or separately compiler forces you to use either
JavaInnerClass
or
JavaClass[TypeParam]#JavaInnerClass

But "JavaClass[TypeParam]#JavaInnerClass" form doesn't work at all when there is an intermediate Java class inherited.

@som-snytt
Copy link

javaFindMember was in 2.12.9/2.13.0

Interface class and interface members are static, so they go in the companion.

The currently improved message is available on selections; maybe it could be extended to some "symbol not found" but at least checking selections in this, or perhaps X.this.

ConcreteImpl.scala:4: error: type InnerInterface is not a member of ConcreteImpl
did you mean OuterInterface.InnerInterface? Static Java members belong to companion objects in Scala;
they are not inherited, even by subclasses defined in Java.
  type II = this.InnerInterface
                 ^

@SethTisue SethTisue removed this from the Backlog milestone Oct 16, 2023
@SethTisue SethTisue added this to the 2.12.9 milestone Oct 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants