Scala Function1<T,U> becomes Function1<Object,Object> in Java

Viewed 57

I am trying to use the Deequ library from Java, and am having issue using the following method:

  def hasCompleteness(
      column: String,
      assertion: Double => Boolean,
      hint: Option[String] = None)
    : CheckWithLastConstraintFilterable = {
    addFilterableConstraint { filter => completenessConstraint(column, assertion, filter, hint) }
  }

In particular, for the assertion, I need to pass in a Function1<Double, Boolean>

So I use the following code to create an assertion function in Java:

public static Function1<Double, Boolean> atLeast(double thresholdPercentage) {
  return new scala.compat.java8.functionConverterImpls.FromJavaFunction<>((Double actualPercentage) -> actualPercentage >= thresholdPercentage);
}

Simple enough, right?

Only, my Java Class now won't compile because the signature of the hasCompleteness method, when viewed from Java is that the assertion parameter is a Function1<Object, Object>. Thus I see the following error:

Required type: Function1<Object, Object>
Provided: Function1<Double, Boolean>

If I change my atLeast method to return an unparametrized Function1 the issue goes away, but this feels like an ugly kludge.

Am I condemned to losing the type params when using this method from Java? Or is there something perhaps wrong with my IntelliJ / Gradle setup causing this weird behavior?

2 Answers

That seems how Scala compiler translates "primitive" Scala types (remember, Double here is scala.Double and not java.lang.Double). E.g. Scala declarations

  var d: Double => Boolean;
  var s: String => String;

produce (as shown by javap):

  public abstract scala.Function1<java.lang.Object, java.lang.Object> d();
  public abstract scala.Function1<java.lang.String, java.lang.String> s();

Note how Double and Boolean both translate to Object, but String is preserved

peterz detailed exactly what happens under the hood.

Sounds like what you need is an explicit conversion from the primitive double Java predicate (java.util.function.DoublePredicate) to the scala.Double Scala predicate (Double => Boolean).

Scala actually offers one such conversion called FromJavaDoublePredicate. The code does not require explicit Double cast anymore:

  /* some scala file */
  def hasCompleteness(
      x: Double,
      f: Double => Boolean
  ): Unit = println(f(x))

  /* some java file */
  public static FromJavaDoublePredicate atLeast(double thresholdPercentage) {
      return new scala.compat.java8.functionConverterImpls.FromJavaDoublePredicate(
        actualPercentage -> actualPercentage >= thresholdPercentage);
  }

  FromJavaDoublePredicate f = atLeast(3.0);
  Test.hasCompleteness(5, f);  // true

PS. From what I saw, you chose scala-java8-compat, so I assumed you use an earlier version of Scala. Their maintainers state:

If you are using Scala 2.13 or newer only, then don't use this library! Use the classes under scala.jdk instead; they were added to the standard library in 2.13.

So for Scala 2.13.x I recommend using the FromJavaDoublePredicate case class in the FunctionWrappers object instead.

Related