Reflection 如何使用反射在scala中创建一个像样的toString()方法?
为了简化类的调试时内省,我想在基类中为相关对象创建一个通用的toString方法。由于它不是性能关键的代码,我想使用反射来打印字段名/值对(“x=1,y=2”等) 有没有一个简单的方法可以做到这一点?我尝试了几种可能的解决方案,遇到了安全访问问题等 为了清楚起见,基类中的toString()方法应该反射地迭代继承自它的任何类中的公共VAL,以及混合在其中的任何特征。示例:Reflection 如何使用反射在scala中创建一个像样的toString()方法?,reflection,scala,tostring,Reflection,Scala,Tostring,为了简化类的调试时内省,我想在基类中为相关对象创建一个通用的toString方法。由于它不是性能关键的代码,我想使用反射来打印字段名/值对(“x=1,y=2”等) 有没有一个简单的方法可以做到这一点?我尝试了几种可能的解决方案,遇到了安全访问问题等 为了清楚起见,基类中的toString()方法应该反射地迭代继承自它的任何类中的公共VAL,以及混合在其中的任何特征。示例: override def toString() = { getClass().getDeclaredFields().m
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.\uz
此外,您可能需要在字段上捕获IllegalAccessException。在某些情况下,get(this)调用,但这只是一个起点。Scala不会生成任何公共字段。他们都是私人的。访问器方法是公开的,反映在这些方法上。给定一个类,如:
class A {
var x = 5
}
生成的字节码如下所示:
private int x;
public void x_$eq(int);
public int x();
用于Scala 2.8.x NameTransformer的导入工具
为scala 2.7.x NameTransformer导入scala.tools.nsc.util.\u//
/**
*反复运行'f',直到它返回None,然后将结果汇编成一个流。
*/
def展开[A](A:A,f:A=>选项[A]):流[A]={
Stream.cons(a,f(a).map(展开(u,f)).getOrElse(Stream.empty))
}
def get[T](f:java.lang.reflect.Field,a:AnyRef):T={
f、 setAccessible(true)
f、 得到(a).a代替[T]
}
/**
*@如果t为null,则返回None,否则返回部分(t)。
*/
def optNull[T optNull(c.getSuperclass))
def showReflect(a:AnyRef):字符串={
val fields=classAndSuperClasses(a.getClass).flatMap(u.getDeclaredFields).filter(!u.isSynthetic)
fields.map((f)=>NameTransformer.decode(f.getName)+“=”+get(f,a)).mkString(“,”)
}
//试验
性状T{
val t1=“t1”
}
类基(val foo:String,val???:Int){
}
派生类(val d:Int)用T扩展基(“foo”,1)
断言(showReflect(新派生的(1))=“t1=t1,d=1,?=1,foo=foo”)
您是否知道Scala case类会获得以下编译器生成的方法:
- toString():字符串
- 等于(其他:任意):布尔值
- hashCode:Int
生成的
toString()
与您描述的非常相似。这不应该是特征吗?trait-toString[a]
?这可能是最干净的方法,我还没有想到它会如何影响我的对象继承权。我有一些属性访问问题,目前无法解释。这里的答案,特别是使用Apache ReflectionStringBuilder,不是一个坏主意:我已经使用了类似的方法,但我获得了很多访问权限我对此感到困惑,因为如果我有这样的代码:class Foo{val bar=3},那么如果我从另一个类调用Foo.getDeclaredField(“bar”),结果字段对象将具有修饰符“public”。我一直在使用java.lang.reflect.Modifier来解码修饰符字段,但它不起作用。这是在2.7.7中,所以我不确定它在这方面是否与2.8有所不同。我发现val obj=field.get(This);val str=obj.asInstanceOf[String]
不起作用。奇怪的是,对于那些使用Scala 2.10的人来说,只要用mkstring替换deepMkString就行了。我相信很久以前我就知道这一点。谢谢!是的,case类很神奇。不幸的是,出于各种原因,我没有在这里使用它们。不过,我确实使用了您在其他地方列出的所有功能。我确信我的toString特性可以做到未来的le case类。另外,昨晚在IRC上很高兴见到你,小世界。我应该提到,我现在坚持使用2.7.7。我不确定你在代码中使用的一些新的2.8结构,结果出现了空指针异常。不过,这看起来是一种相当干净的方法,所以答案就在你身上!
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")