Domanda

Per fare il debug in tempo introspezione in classi facile, mi piacerebbe fare un metodo toString generico nella classe base per gli oggetti in questione. Poiché non è il codice prestazioni critiche, mi piacerebbe usare riflessione per stampare le coppie nome del campo / valore ( "x = 1, y = 2", ecc).

C'è un modo semplice per fare questo? Ho provato diverse soluzioni possibili, e corse contro problemi di accesso di sicurezza, ecc.

Per essere chiari, il metodo toString () nella classe base deve riflessivo iterare vals pubblici in tutte le classi che ereditano da essa, così come eventuali tratti che sono mescolati in.

È stato utile?

Soluzione

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

Altri suggerimenti

Esempio:

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

utilizza Java Reflection API, quindi non dimenticate di Importa java.lang.reflect ._

Inoltre, potrebbe essere necessario per la cattura di IllegalAccessException sul field.get (questo) chiama in alcuni scenari, ma questo è solo inteso come un punto di partenza.

Sei consapevole le classi case Scala ottenere questi metodi generati dal compilatore:

  • toString (): String
  • è uguale a (altro: Qualsiasi): booleano
  • hashCode: Int

Ma hanno anche un compagno di oggetti per la "new-less" matching costruttori e modello.

Il toString() generato è più o meno simile a quella che si descrive.

Scala non genera campi pubblici. Sono tutti andando a essere privato. I metodi di accesso sono quello che sarà pubblica, riflettere su quelli. Data una classe come:

class A {
  var x = 5
}

Il bytecode generato appare come:

private int x;
public void x_$eq(int);
public int x();
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top