Pregunta

Para facilitar la introspección en el tiempo de depuración en clases, me gustaría hacer un método genérico de tostración en la clase base para los objetos en cuestión. Como no es un código crítico de rendimiento, me gustaría usar la reflexión para imprimir pares de nombre/valor de campo ("x = 1, y = 2", etc.).

¿Hay una forma fácil de hacer esto? Probé varias soluciones potenciales y corrí contra los problemas de acceso de seguridad, etc.

Para ser claros, el método toString () en la clase base debe iterar reflexivamente sobre vals públicos en cualquier clases que herede de él, así como cualquier rasgo que se mezcle.

¿Fue útil?

Solución

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")

Otros consejos

Ejemplo:

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

Utiliza la API de reflexión de Java, así que no olvides importar java.lang.reflect._

Además, es posible que deba atrapar Ilegalaccessexception En el campo. Pet (esto) llame en algunos escenarios, pero esto se entiende como un punto de partida.

¿Sabe que las clases de casos Scala obtienen estos métodos generados por compiladores?

  • toString (): cadena
  • iguales (otro: cualquiera): booleano
  • hashcode: int

También obtienen objetos complementarios para constructores "nuevos" y coincidencia de patrones.

El generado toString() es más o menos el que describe.

Scala no genera ningún campo público. Todos van a ser privados. Los métodos de accesorios son lo que será público, reflexionan sobre ellos. Dada una clase como:

class A {
  var x = 5
}

El bytecode generado parece:

private int x;
public void x_$eq(int);
public int x();
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top