如何使用反射在scala中创建一个体面的toString()方法?

时间:2010-01-06 21:45:29

标签: reflection scala tostring

为了简化类的调试时内省,我想在基类中为相关对象创建一个通用的toString方法。由于它不是性能关键代码,我想使用Reflection来打印字段名称/值对(“x = 1,y = 2”等)。

有一种简单的方法吗?我尝试了几种可能的解决方案,并遇到了安全访问问题等等。

要明确的是,基类中的toString()方法应该反复迭代从它继承的任何类中的公共val,以及混合的任何特征。

4 个答案:

答案 0 :(得分:6)

示例:

override def toString() = {
  getClass().getDeclaredFields().map { field:Field => 
    field.setAccessible(true)
    field.getName() + ": " + field.getType() + " = " + field.get(this).toString()
  }.deepMkString("\n")
}

使用Java Reflection API,所以不要忘记 import java.lang.reflect ._

此外,在某些情况下,您可能需要在field.get(this)调用上捕获 IllegalAccessException ,但这只是一个起点。

答案 1 :(得分:4)

您是否知道Scala案例类可以获得这些编译器生成的方法:

  • toString():String
  • equals(other:Any):Boolean
  • hashCode:Int

他们还获得了“new-less”构造函数和模式匹配的伴随对象。

生成的toString()与您描述的非常相似。

答案 2 :(得分:2)

Scala不会生成任何公共字段。他们都是私人的。存取方法是公开的,反映在那些方面。给出类似的类:

class A {
  var x = 5
}

生成的字节码如下所示:

private int x;
public void x_$eq(int);
public int x();

答案 3 :(得分:2)

import util._                 // For Scala 2.8.x NameTransformer
import scala.tools.nsc.util._ // For Scala 2.7.x NameTransformer

/**
 * Repeatedly run `f` until it returns None, and assemble results in a Stream.
 */
def unfold[A](a: A, f: A => Option[A]): Stream[A] = {
  Stream.cons(a, f(a).map(unfold(_, f)).getOrElse(Stream.empty))
}

def get[T](f: java.lang.reflect.Field, a: AnyRef): T = {
  f.setAccessible(true)
  f.get(a).asInstanceOf[T]
}

/**
 * @return None if t is null, Some(t) otherwise.
 */
def optNull[T <: AnyRef](t: T): Option[T] = if (t eq null) None else Some(t)

/**
 * @return a Stream starting with the class c and continuing with its superclasses.
 */
def classAndSuperClasses(c: Class[_]): Stream[Class[_]] = unfold[Class[_]](c, (c) => optNull(c.getSuperclass))

def showReflect(a: AnyRef): String = {
  val fields = classAndSuperClasses(a.getClass).flatMap(_.getDeclaredFields).filter(!_.isSynthetic)
  fields.map((f) => NameTransformer.decode(f.getName) + "=" + get(f, a)).mkString(",")
}

// TEST
trait T {
  val t1 = "t1"
}

class Base(val foo: String, val ?? : Int) {
}

class Derived(val d: Int) extends Base("foo", 1) with T

assert(showReflect(new Derived(1)) == "t1=t1,d=1,??=1,foo=foo")