ferics2
ferics2

Reputation: 5432

How to pass nested scala object reference in Java?

There have been some questions answered on this before.

But my problem is that I have nested scala objects, something like:

object Criteria {
    object ActionCriteria {
        case class Action (parameter: String) {
            def this(parameter: String) = { this(paramerter) }
        }

        object Action {
           def apply(parameter: String): Action = { apply(parameter) }
        } 
    }
}

In java I then need to create a list of Actions. I have tried this... to no avail:

import Criteria.ActionCriteria.Action$

....

List<Criteria.ActionCriteria.Action$.MODULE$> actions = new ArrayList<>();

As well as a bunch of other combinations like adding $.MODULE$ with every object. Right now I am getting the following error:

error: cannot find symbol Criteria.ActionCriteria

Upvotes: 3

Views: 1115

Answers (1)

Paweł Bartkiewicz
Paweł Bartkiewicz

Reputation: 800

List<Criteria$ActionCriteria$Action> actions = new ArrayList<>();

Seems to work fine. Found this with Scala REPL:

scala> classOf[Criteria.ActionCriteria.Action]
res1: Class[Criteria.ActionCriteria.Action] = class Criteria$ActionCriteria$Action

If you want the type of Action object, not case class (highly unlikely, but for the sake of completeness):

scala> Criteria.ActionCriteria.Action.getClass
res2: Class[_ <: Criteria.ActionCriteria.Action.type] = class Criteria$ActionCriteria$Action$

The difference is caused by Scala expecting Action to be a type in classOf[Action], so it returns the type corresponding to the case class. When you use Action in a context where a value is expected, it returns the singleton instance instead, so you can call standard Java method getClass to get the type of object Action.

In case you need other types:

Criteria$                       cm    = Criteria$.MODULE$;
Criteria.ActionCriteria$        cacm  = Criteria.ActionCriteria$.MODULE$;
Criteria$ActionCriteria$Action$ cacam = Criteria$ActionCriteria$Action$.MODULE$;
Criteria$ActionCriteria$Action  caca  = new Criteria$ActionCriteria$Action("Foo");

Criteria.ActionCriteria$ is breaking the pattern here. Why? According to Iulian Dragos' comment under bug SI-2034 this is a special case:

since objects are "the equivalent of static" in the Java world, we wanted to make it easier for Java code to use static inner classes. When there's only one level of nesting, there's a guaranteed companion: every top-level object gets a mirror class (if there isn't one) that creates static forwarders to module methods (that's how one can run a main method defined inside an object). Therefore, a special case for one-level nesting: those classes use the flattened name (without a $ suffix) as outer_name. So, Java code can say new Outer.Inner.


Summary

  • For every level of nesting other than first you replace . with $ in your class names

  • If the target type is also an object you add $ at the end

  • If you want an instance you add .MODULE$

Upvotes: 4

Related Questions