I'm having trouble understanding why using scala's runtime reflection in 2.11.1 gives me seemingly inconsistent results.
I am trying to inspect the type of a field contained in a java object, like this one:
import java.util.List;
import java.util.ArrayList;
public class Example {
private List<Integer> listOfInts;
public Example () {
listOfInts = new ArrayList<Integer>();
}
}
Now suppose I have a scala program that tries to reason about the type of the field inside "Example:"
import java.lang.Class
import java.lang.reflect.Field
import java.util.List
import scala.reflect.runtime.{ universe => ru }
object Inspect extends scala.App {
val example = new Example
val cls = example.getClass
val listfield = cls.getDeclaredField("listOfInts")
println(isListType(listfield)) // prints false
println(isListType(listfield)) // prints true, as do all subsequent calls
def isListType (field: Field): Boolean = {
/*
A function that returns whether the type of the field is a list.
Based on examples at http://docs.scala-lang.org/overviews/reflection/environment-universes-mirrors.html
*/
val fieldcls = field.getType
val mirror: ru.Mirror = ru.runtimeMirror(getClass.getClassLoader)
val fieldsym: ru.ClassSymbol = mirror.classSymbol(fieldcls)
val fieldtype: ru.Type = fieldsym.toType
(fieldtype <:< ru.typeOf[List[_]])
}
}
In this particular code snippet, the first call to isListType returns false, and the second returns true. If I switch the type operator from <:<
to =:=
, the first call returns true, and the second false.
I have a similar function in a larger code body, and have found that even when the function is part of a static object, this behavior occurs. This does not happen when using unparameterized classes. While I intended for the function to be pure, this is obviously not the case. Further experimentation has shown that there is some persistent state held somewhere. If I replace the isListType
function with straightline code, I get this:
...
val example = new Example
val cls = example.getClass
val listfield = cls.getDeclaredField("listOfInts")
val fieldcls = listfield.getType
val mirror: ru.Mirror = ru.runtimeMirror(getClass.getClassLoader)
val fieldsym: ru.ClassSymbol = mirror.classSymbol(fieldcls)
val fieldtype: ru.Type = fieldsym.toType
println(fieldtype <:< ru.typeOf[List[_]]) // prints false
println(fieldtype <:< ru.typeOf[List[_]]) // prints false
but if I reassign to field type after the <:<
operator, I get this:
// replace as under the fieldsym assignment
var fieldtype: ru.Type = fieldsym.toType
println(fieldtype <:< ru.typeOf[List[_]]) // prints false
fieldtype = fieldsym.toType
println(fieldtype <:< ru.typeOf[List[_]]) // prints true
while reassigning to field type before the <:<
operator gives this:
// replace as under the fieldsym assignment
var fieldtype: ru.Type = fieldsym.toType
fieldtype = fieldsym.toType
println(fieldtype <:< ru.typeOf[List[_]]) // prints false
println(fieldtype <:< ru.typeOf[List[_]]) // prints false
Does anyone understand what I'm doing wrong here, or at least have a way around this?
The reflection library is based on the compiler, which is a crying shame. People should demand better. Anyway, this is just how it is.
Here's a sample ticket from almost two years ago. https://issues.scala-lang.org/browse/SI-6826
There's almost nothing but.
Addendum: for a truly dizzying experience, page through a selection of the 355 open reflection tickets.
I don't know about mixing Java reflection and Scala reflection, but symbols do need initialization, as I remember past issues around lack thereof. Surely the re-assignment is not relevant, but maybe after the first
<:<
the state of the Symbol changes.usage:
Sorry if I'm too distracted to read your question correctly.
Here's my attempt to reproduce:
Another attempt:
OK, so I verified your test class. If the REPL behaves differently, it's probably due to side effects because of printing results.
So adding this to your test
fixes the issue.
I don't know if there's a lesson about "initialize your Syms!"
"At Syms, an educated consumer is our best customer."