Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create new instance of Scala class with context bound via Java reflection using only zero argument constructor?

I am writing client code in Scala that needs to interface with a framework in Java. The framework is responsible for creating object instances of classes specified via an API, which it does using reflection. For example:

public class ReflectionUtil {

  public static <T> T newInstance(Class<T> aClass) {
    T result;
    try {
      Constructor<T> meth = aClass.getDeclaredConstructor(new Class[]{});
      meth.setAccessible(true);
      result = meth.newInstance();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    return result;
  }
}

The classes of the object instances I want to create are implemented in Scala and are paramerterised on a type that has a context bound on it. For example:

class OrderedValue[A](var value: A)(implicit ord: Ordering[A]) {
  def get: A = value
  def set(x: A) = { value = x }
  def cmp(that: OrderedValue[A]): Int = ord.compare(this.value, that.value)
}

I run into a problem when I pass this class to the Java framework to construct new instances as the framework makes the assumption that the class will have a zero-argument constructor available. For example, the following code will result in a NoSuchMethodException from within newInstance:

def main(args: Array[String]) {

  val a: OrderedValue[Int] = ReflectionUtil.newInstance(classOf[OrderedValue[Int]])
  val b: OrderedValue[Int] = ReflectionUtil.newInstance(classOf[OrderedValue[Int]])

  a.set(3)
  b.set(5)

  println(a.cmp(b))
}

An attempt at resolving this issue is to add a zero-argument constructor to OrderedValue however there is no reasonable value for the implicit parameter ord. Setting it to null will result in a NullPointerException within cmp:

def this() = this(null.asInstanceOf[A])(null.asInstanceOf[Ordering[A]])

Another approach is to subclass a particular concrete value of OrderedValue. For example:

class OrderedIntValue(val v: Int) extends OrderedValue[Int](v) {
  def this() = this(null.asInstanceOf[Int])
}

val a: OrderedValue[Int] = ReflectionUtil.newInstance(classOf[OrderedValue[Int]])

This will work but is not ideal as it is not always convenient or possible to know the concrete type of OrderedValue. For example, newInstance may be called within a scope that is also parameterised on a type (i.e. we don't know that it's specifically an Int).

So my question is: given that context bounds (i.e. type classes) are a very useful, and now commonly used, feature within Scala, and given I can not change the internals of the Java framework that I am interfacing with, has anyone encountered or developed an approach that can make this all work?

like image 778
Ben Lever Avatar asked Dec 27 '22 15:12

Ben Lever


1 Answers

Implicit arguments are filled in by the Scala compiler at the compile time. If you want to instantiate classes using reflection you will have to specify those arguments manually. There is just no way around it. So you can either have context bounds or no-argument constructors.

like image 101
Lex Avatar answered Jan 11 '23 12:01

Lex