我无法理解为什么在2.11.1中使用scala的运行时反射会给我带来看似不一致的结果。
我正在尝试检查java对象中包含的字段的类型,如下所示:
import java.util.List;
import java.util.ArrayList;
public class Example {
private List<Integer> listOfInts;
public Example () {
listOfInts = new ArrayList<Integer>();
}
}
现在假设我有一个scala程序试图推断“示例:”中的字段类型
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[_]])
}
}
在这个特定的代码片段中,对isListType的第一次调用返回false,第二次调用返回true。如果我将类型运算符从<:<
切换到=:=
,则第一个调用返回true,第二个调用返回false。
我在更大的代码体中有类似的功能,并且发现即使函数是静态对象的一部分,也会发生这种情况。使用非参数化类时不会发生这种情况。虽然我打算让这个功能变得纯粹,但事实并非如此。进一步的实验表明,某些地方存在某种持久状态。如果我用直线代码替换isListType
函数,我会得到:
...
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
但如果我在<:<
运算符后重新分配字段类型,我会得到:
// 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
在<:<
运算符之前重新分配到字段类型时:
// 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
有没有人明白我在这里做错了什么,或者至少有办法解决这个问题?
答案 0 :(得分:2)
反射库基于编译器,这是一个令人遗憾的耻辱。人们应该要求更好。无论如何,这就是它的原因。
这是近两年前的样票。 https://issues.scala-lang.org/browse/SI-6826
某处持有某种持久状态
几乎没有。但
附录:要获得真正令人眼花缭乱的体验,请选择355张open reflection门票。
答案 1 :(得分:1)
我不知道混合Java反射和Scala反射,但是符号确实需要初始化,因为我记得过去缺乏的问题。当然,重新分配是不相关的,但可能在符号状态的第一个<:<
发生变化之后。
用法:
scala> import reflect.runtime._ ; import universe._
import reflect.runtime._
import universe._
scala> typeOf[jex.Example]
res0: reflect.runtime.universe.Type = jex.Example
scala> .declarations
warning: there was one deprecation warning; re-run with -deprecation for details
res1: reflect.runtime.universe.MemberScope = SynchronizedOps(variable listOfInts, constructor Example)
scala> typeOf[jex.Example] member (TermName("listOfInts"))
res2: reflect.runtime.universe.Symbol = variable listOfInts
scala> .typeSignature
res3: reflect.runtime.universe.Type = java.util.List[Integer]
很抱歉,如果我太分心了,无法正确阅读您的问题。
这是我尝试重现:
scala> import reflect.runtime._ ; import universe._
import reflect.runtime._
import universe._
scala> classOf[jex.Example].getDeclaredField("listOfInts").getType
res0: Class[_] = interface java.util.List
scala> currentMirror classSymbol res0 toType
warning: there was one feature warning; re-run with -feature for details
res1: reflect.runtime.universe.Type = java.util.List
scala> .<:<(typeOf[List[_]])
res2: Boolean = false
scala> currentMirror classSymbol res0 toType
warning: there was one feature warning; re-run with -feature for details
res3: reflect.runtime.universe.Type = java.util.List[E]
scala> .<:<(typeOf[List[_]])
res4: Boolean = false
另一次尝试:
scala> import reflect.runtime._ ; import universe._
import reflect.runtime._
import universe._
scala> val x = new jex.Example
x: jex.Example = jex.Example@1efed156
scala> x.getClass getDeclaredField "listOfInts" getType
warning: there was one feature warning; re-run with -feature for details
res0: Class[_] = interface java.util.List
scala> val m = runtimeMirror(getClass.getClassLoader)
m: reflect.runtime.universe.Mirror = JavaMirror with scala.tools.nsc.interpreter.IMain$TranslatingClassLoader@1ffd0e4b of type class scala.tools.nsc.interpreter.IMain$TranslatingClassLoader with classpath [(memory)] and parent being scala.reflect.internal.util.ScalaClassLoader$URLClassLoader@3b084709 of type class scala.reflect.internal.util.ScalaClassLoader$URLClassLoader with classpath [file:/home/apm/jdk-8/jdk1.8.0_11/jre/lib/resources.jar,file:/home/apm/jdk-8/jdk1.8.0_11/jre/lib/rt.jar,file:/home/apm/jdk-8/jdk1.8.0_11/jre/lib/jsse.jar,file:/home/apm/jdk-8/jdk1.8.0_11/jre/lib/jce.jar,file:/home/apm/jdk-8/jdk1.8.0_11/jre/lib/charsets.jar,file:/home/apm/jdk-8/jdk1.8.0_11/jre/lib/jfr.jar,file:/home/apm/scala-2.11.2/lib/akka-actor_2.11-2.3.4.jar,file:/home/apm/scala-2.11.2/lib/config-1.2...
scala> val s = m classSymbol res0
s: reflect.runtime.universe.ClassSymbol = trait List
scala> var t = s.toType
t: reflect.runtime.universe.Type = java.util.List[E]
scala> t <:< typeOf[List[_]]
res1: Boolean = false
scala> t = s.toType
t: reflect.runtime.universe.Type = java.util.List[E]
scala> t <:< typeOf[List[_]]
res2: Boolean = false
好的,所以我验证了你的测试课程。如果REPL表现不同,可能是由于打印结果造成的副作用。
将此添加到您的测试中
val fieldsym: ru.ClassSymbol = mirror.classSymbol(fieldcls)
println(fieldsym)
val fieldtype: ru.Type = fieldsym.toType
println(fieldtype)
解决了这个问题。
apm@mara:~/tmp$ vi jex/inspect.scala
apm@mara:~/tmp$ scalac jex/inspect.scala && scala jex.Inspect
false
true
apm@mara:~/tmp$ vi jex/inspect.scala
apm@mara:~/tmp$ scalac jex/inspect.scala && scala jex.Inspect
trait List
java.util.List[E]
true
trait List
java.util.List[E]
true
我不知道是否有关于&#34;初始化您的Syms的课程!&#34;
&#34;在Syms,受过良好教育的消费者是我们最好的客户。&#34;