Help with org.reflections api

I’m using a library called reflections. But I don’t quite know enough about what’s happening at the Java level to understand what does/should happen when calling it from Scala.

I can use the API to examine classes which I use in a Scala program, and I can use it to examine some Java classes such as java.lang.Number.

For example the following code figure out that MyNumber is a public subclass of java.lang.Number and prints the following output:

--------------
class java.lang.Number is public abstract
class genus.sanityCheck$MyNumber$1 is public
---------
class scala.collection.immutable.List is public abstract

object sanityCheck {
  val refl = new org.reflections.Reflections("")
  def main(argv:Array[String]):Unit = {
    describeSubclasses(classOf[java.lang.Number])
    makeNumber()
    describeSubclasses(classOf[List[Any]])

  }
  def describeSubclasses(cl:Class[_]):Unit = {
    import java.lang.reflect.Modifier
    // getSubTypesOf does not know a class is a subclass of itself. so we use ::
    val subs:List[Class[_]] = cl :: refl.getSubTypesOf(cl).toArray.toList.collect {
      case c: Class[_] => c
    }
    println("---------")
    for{ sub <- subs
         } println( s"$sub is " + Modifier.toString(sub.getModifiers))

  }
  def makeNumber():Unit = {
    class MyNumber extends Number {
      def doubleValue():Double = 0.0
      def longValue():Long = 0
      def intValue():Int = 0
      def floatValue():Float = 0.0F
    }
    new MyNumber
  }
}

However, I can’t figure out to to get meaningful information about a class such as scala.collection.immutable.List. When I try to examine classOf[List[Any]], I find that it is also a public abstract class but has no subclasses.

Is this just a limitation of the reflections API that it is unable to find subclasses of parametrized scala classes? Or is there really something fundamentally different about List[Any] which means there can be instances of it even though it has no instantiatable subclasses?

1 Like

Really? If I run your program it prints out:

class scala.collection.immutable.List is public abstract
class scala.collection.immutable.$colon$colon is public final
class scala.collection.immutable.Nil$ is public final

:: and Nil are indeed the only subclasses of List, according to the scaladoc.

1 Like

which version of reflection are you using? I had to revert from 0.9.12 to 0.9.11 because of some
issue which I’ve forgotten the details of now.

libraryDependencies += "org.reflections" % "reflections" % "0.9.11"

I just used the newest: 0.9.12

1 Like

hmm. when up update to 0.9.12 I still don’t see any subclasses of List[Any]. So it sounds like what I’m trying to do is supposed to work. Either an issue with my project setup or just a bug in the reflections library.

---------
class java.lang.Number is public abstract
class genus.sanityCheck$MyNumber$1 is public
---------
class scala.collection.immutable.List is public abstract

Which scala version and which java version are you using @cbley?

AdoptOpenJDK Java 11.0.7, Scala 2.12.10

edit: Also works with 2.13.6 for me.

I wonder what could be the difference? I’ve created an issue, but I’m 99% they won’t be able to tell me anything as I don’t have enough information to reproduce the problem.

Probably some kind of classpath issue… How are you compiling / running your code?

I’m just compiling and running it by pressing the little green go-triangle in IntelliJ.
Is that bad?

It’s not bad, but it could make a difference. Maybe you could show your run configuration

Do these assertions work for you?

    val reflect = new org.reflections.Reflections("")
    assert(reflect.getSubTypesOf(classOf[List[Any]]).toArray.contains(List(1,2,3).getClass))
    assert(reflect.getSubTypesOf(classOf[List[Any]]).toArray.contains(List.empty.getClass))

Take a look here at this scastie test case
It seems to fail there as well.

Yes, this works for me.

I downloaded the scastie example and tried to run it, but it cannot find the scastie plugin and runtime dependencies:

[warn] 	Note: Unresolved dependencies path:
[error] sbt.librarymanagement.ResolveException: Error downloading org.scastie:sbt-scastie;sbtVersion=1.0;scalaVersion=2.12:1.0.0-SNAPSHOT

and

[warn] 	Note: Unresolved dependencies path:
[error] sbt.librarymanagement.ResolveException: Error downloading org.scastie:runtime-scala_2.13:1.0.0-SNAPSHOT

So, I commented the plugin in project/plugins.sbt and removed the dependency on scastie’s scala-runtime. Also, wrapped the code in main.scala into an object extending App.

Now, sbt run works just fine.

1 Like

In scastie does the link I gave you already include this in the extra sbt configuration?

libraryDependencies += "org.reflections" % "reflections" % "0.9.12"

I got an exception in the scatie link.

java.lang.AssertionError: assertion failed
	at scala.Predef$.assert(Predef.scala:265)
	at Playground$.delayedEndpoint$Playground$1(main.scala:10)
	at Playground$delayedInit$body.apply(main.scala:2)
	at scala.Function0.apply$mcV$sp(Function0.scala:39)
	at scala.Function0.apply$mcV$sp$(Function0.scala:39)
	at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:17)
	at scala.App.$anonfun$main$1(App.scala:76)
	at scala.App.$anonfun$main$1$adapted(App.scala:76)
	at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:563)
	at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:561)
	at scala.collection.AbstractIterable.foreach(Iterable.scala:919)
	at scala.App.main(App.scala:76)
	at scala.App.main$(App.scala:74)
	at Playground$.main(main.scala:2)
	at Main$.main(main.scala:24)
	at Main.main(main.scala)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sbt.Run.invokeMain(Run.scala:133)
	at sbt.Run.execute$1(Run.scala:82)
	at sbt.Run.$anonfun$runWithLoader$5(Run.scala:110)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at sbt.util.InterfaceUtil$$anon$1.get(InterfaceUtil.scala:17)
	at sbt.TrapExit$App.run(TrapExit.scala:258)
	at java.lang.Thread.run(Thread.java:748)

Yes it did.

And it also fails in scastie for me. That’s why I wanted to try it locally; it’s a pity one cannot reproduce problems with scastie since they are using dependencies sbt cannot resolve…

But, what happens if you download the scastie example and comment out the two lines I mentioned and make the example a runnable application… Does it work for you then @jimka ?

@jimka Changing the initialization to restrict scanning of the classpath to the “scala” namespace fixes it for me:

val refl = new org.reflections.Reflections("scala")

See Scastie - An interactive playground for Scala

That seems to indicate a bug in the reflections library…