无法使用getDeclaredFields()来检索Scala类的字段

时间:2012-08-07 19:44:42

标签: java scala reflection

我试图在Scala中使用Java库(JOhm)并注意到当lib尝试使用类似model.getClass().getDeclaredFields()的内容读取Scala类的字段时,它会失败。

然后我决定尝试使用Scala解释器中的简单示例:

scala> import java.lang.reflect.Field;
import java.lang.reflect.Field

scala> class myClass(attribute1: String, attribute2: String, attribute3: String)
defined class myClass

scala> val myInstance = new myClass("value1", "value2", "value3")
myInstance: myClass = myClass@7055c39a

scala> myInstance.getClass().getDeclaredFields()
res0: Array[java.lang.reflect.Field] = Array()

事实上,我们根本没有任何领域。

现在,如果我尝试这个怎么办:

scala> class myClass2(attribute1: String, attribute2: String, attribute3: String) { override def toString = this.attribute1 }
defined class myClass2

scala> val myInstance2 = new myClass2("value1", "value2", "value3")
myInstance2: myClass2 = value1

scala> myInstance2.getClass().getDeclaredFields()
res1: Array[java.lang.reflect.Field] = Array(private final java.lang.String myClass2.attribute1)

因此,如果使用其中一个类别中的一个字段'方法,它由getDeclaredFields()找到。我在这里缺少什么?

3 个答案:

答案 0 :(得分:7)

您缺少的是构造函数参数不会自动提升为字段

相反,它们只有在使用时才会被提升。您使用了attribute1,因此它变成了一个字段;你没有使用其他人,所以他们没有。

如果您将它们声明为valvar,或者该类是一个案例类,它们也将被提升为字段(因为它们实际上会生成存取方法,因此会被使用)

答案 1 :(得分:1)

如果您将字段标记为valvar,则getDeclaredFields会找到它们,例如

class myClass(val attribute1: String)

getFields的JavaDoc表示它返回“所有可访问的公共字段”,因此除非将字段显式公开(默认情况下,构造函数参数为私有值),否则不会列出字段。但是,getDeclaredFields的JavaDoc没有提到这样的限制,但字段的可见性显然也在这里产生影响。


修改以回应@Clément:

import java.lang.reflect.Field

class Foo(val a1: String, private val a2: String, a3: String, a4: String) {
  val f = 10
  def foo(s: String) = a4 + s
}

val foo = new Foo("v1", "v2", "v3", "v4")

foo.getClass().getDeclaredFields().foreach(println)
  /* {a1, a2, a4, f} */

foo.getClass().getFields().foreach(println)
  /* {} */

答案 2 :(得分:0)

我的猜测是,这是因为Scala编译器不会为所有构造函数参数生成字段。如果您向类定义添加varval,则会生成字段:

scala> class myClass3(val attribute1:String, attribute2:String, attribute3:String)
defined class myClass3

scala> val myInstance3 = new myClass3("value1", "value2", "value3")
myInstance: myClass3 = myClass3@fd9178

scala> myInstance3.getClass().getDecalaredFields()
res1: Array[java.lang.reflect.Field] = Array(private field java.lang.String myClass3.attribute1)

请注意,对于最后两个构造函数参数,没有生成字段。那是因为它们仅仅是构造函数参数而且什么都不做。我认为覆盖toString函数时它起作用的原因实际上只是因为编译器生成了一个隐藏字段,该字段在toString方法中访问参数时使用。我不认为这应该依赖。最好明确说明你的字段是什么构造函数参数。