In Scala, fetched value of declared field cast to

2019-08-05 08:38发布

问题:

I would like to ask how to achieve the following in Scala. Consider

scala> case class C(i:Int)
defined class C

scala> val c = C(1)
c: C = C(1)

Given a field of interest, in this case

scala> val fname = "i"
fname: String = i

we would like to retrieve the original value and type of field i in c.

A first, naive, attempt included the following,

scala> val f = c.getClass.getDeclaredField(fname)
f: java.lang.reflect.Field = private final int C.i

scala> f.setAccessible(true)

scala> f.getType
res3: Class[_] = int

However,

scala> val a:Int = f.get(c)
<console>:11: error: type mismatch;
 found   : Object
 required: Int
       val a:Int = f.get(c)
                        ^

Put another way, how to fetch the Int value for i in c (*)

scala> :type -v case class C(i:Int)
// Type signature
AnyRef
        with Product
        with Serializable {
  val i: Int  <----------------------- (*)
  private[this] val i: Int
  def <init>(i: Int): C
  def copy(i: Int): C
...

and for not necessarily Int type, consider field j in D,

scala> case class C(i:Int)
defined class C

scala> case class D(j:C)
defined class D

scala> :type -v case class D(j:C)
// Type signature
AnyRef
        with Product
        with Serializable {
  val j: C
  private[this] val j: C
  def <init>(j: C): D
  def copy(j: C): D
...

Thanks very much...

In Summary

Given

scala> f.get(c)
res1: Object = 1

and

scala> f.getType
res3: Class[_] = int

how to get

val a = 1

where a is of type Int, and knowing the type only from f.getType.

回答1:

The static type of f.get(c) is Object, because it can be any class and any field. However, at the runtime it will return an Integer (the Java wrapper class for Int). You can cast it by using

f.get(c).asInstanceOf[Int]

or

f.getInt(c)

if you know in advance that you are calling for an Int field. If you don't, you can pattern match:

f.get(c) match {
  case i: Integer => ...
  case l: java.lang.Long => ...
  case s: String => ...
  // etc.
}

// actually compiles to same code, but avoids the need to use boxed classes
(f.get(c): Any) match {
  case i: Int => ...
  case l: Long => ...
  case s: String => ...
  // etc.
}

Note that the taken branch depends on the actual value of the field, not on its type; e.g. for val f: Any = "" the case s: String branch will be taken.

Or you can use f.getType to get its type and make your logic depend on that.