Why are nested Java classes not importable from Scala?


#1

How am I supposed to mock a nested Java class using scalamock, especially when said nested Java class is coming from a third party library?

Given the following sources:

src/main/java/Outer.java

/**
  * Outer class that offers a Nested class inaccessible from Scala :(
  */
 public class Outer {
   public class Nested {

   }
 }

src/main/java/UseNestedJavaClassFromJava.java

public class UseNestedJavaClassFromJava {
   private Outer.Nested nested;
 }

src/main/scala/ImportNestedJavaClass.scala

// TODO uncomment the below line to break the build
//import Outer.Nested

Uncommenting the scala import line results in a compilation failure while compiling UseNestedJavaClassFromJava.java works just fine.

Full minimal example with gradle: https://github.com/billyjf/async-http-client-gradle-scala.

Apparently this was somewhat already addressed in the below question, but resorting to Java glue code or reflection trickery just for the sake of testing Scala code that leverages a Java library with nested Java classes seems a bit unreasonable to me, is there really no other solution?

While I did settle on the following solution, I lucked out in that all I needed currently was a mock.

I suppose reflection would still work in cases where constants need to be accessed in a third party libraries’ nested java class, but wow is that counterintuitive.

Where could I learn more about contributing to Scala’s Java interoperability?

Scalatest Mockito solution:

import org.mockito.Mockito
import org.scalamock.scalatest.MockFactory
import org.scalatest.mockito.MockitoSugar
import org.scalatest.{FlatSpec, Matchers}

class OuterNestedTest extends FlatSpec with MockFactory with Matchers with MockitoSugar {
  "Nested Java class" should "be mockable using Mockito from within a scalatest" in {
    val mockedNestedJavaClass = Mockito.mock(classOf[Outer#Nested])

    Mockito.when(mockedNestedJavaClass.methodWithAParam("some value"))
      .thenReturn("mocked", Nil: _*)

    mockedNestedJavaClass.methodWithAParam("some value") shouldBe "mocked"
  }
}

#2

For future readers’ benefit: you also asked this at https://stackoverflow.com/questions/52132456/why-are-nested-java-classes-not-importable-from-scala

And the reason why they can’t be imported in the way you want is exactly the same as for Scala nested classes. A nested (non-static) class is tied to an instance of the outer class. So Outer.Nested is not a stable, importable path because Outer is not an instance nor a package.

What works is

val out = new Outer
val n1 = new out.Nested

import out.Nested
val n2 = new Nested