How to create new instance of Scala class with context bound via Java reflection using only zero argument constructor?
I am writin开发者_开发知识库g 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?
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.
I found that the easiest way of calling Scala code from Java is to write an intermediate Scala layer with the Scala equivalent of POJOs. No implicits, no closures in signatures, no Companion object, no complicated type inference, etc. (of course, you can use these internally). I also try to replace most scala Collections types in signatures with java.util Collections.
Yes, it is ugly, tedious and not very flexible, but at least it removes a lot of syntax nightmare from the Java side.
精彩评论