如何使用反射在Scala中创建一个体面的ToString()方法?
-
19-09-2019 - |
题
为了使调试时间内省为课程,我想在相关对象的基类中制作一种通用的ToString方法。由于不是性能关键代码,我想使用反射来打印字段名称/值对(“ x = 1,y = 2”等)。
是否有捷径可寻?我尝试了几种潜在的解决方案,并反对安全访问问题等。
需要明确的是,基类中的ToString()方法应在任何从中继承的类别以及混合的任何特征中的公共vals进行反思。
解决方案
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")
其他提示
例子:
override def toString() = {
getClass().getDeclaredFields().map { field:Field =>
field.setAccessible(true)
field.getName() + ": " + field.getType() + " = " + field.get(this).toString()
}.deepMkString("\n")
}
使用Java反射API,所以不要忘记 导入java.lang.reflect._
另外,您可能需要抓住 Illegalaccessexception 在字段上。(this)在某些情况下调用,但这只是起点。
您是否知道Scala案例类获取这些编译器生成的方法:
- tostring():字符串
- 等于(其他:任何):布尔值
- 哈希码:int
他们还获得了“新的”构造函数和图案匹配的伴侣对象。
生成的 toString()
就像您描述的那个。
Scala不会生成任何公共字段。他们都将成为私人。登录方法是公开的,反思。给予类似的课程:
class A {
var x = 5
}
生成的字节码看起来像:
private int x;
public void x_$eq(int);
public int x();
不隶属于 StackOverflow