6
votes

Comment créer une méthode de tostring () décente dans SCALA en utilisant la réflexion?

Pour faciliter l'introspection du temps de débogage dans les classes, je voudrais effectuer une méthode de totrage générique dans la classe de base pour les objets en question. Comme il n'est pas du code critique de la performance, j'aimerais utiliser la réflexion pour imprimer des paires de noms / valeur de champ ("x = 1, y = 2", etc.).

Y a-t-il un moyen facile de faire cela? J'ai essayé plusieurs solutions potentielles et avons couru contre les problèmes d'accès à la sécurité, etc.

Pour être clair, la méthode Tostring () dans la classe de base doit réfléchir à la réflexion sur les vals publics dans toutes les classes qui héritent de celle-ci, ainsi que de tous les traits mélangés.


3 commentaires

Ne devrait-il pas être un trait? TRAIT TOSTRING [A] ?


Cela pourrait être le moyen le plus propre de le faire, je n'ai pas pensé à la manière dont cela a une incidence sur mon héritière de l'objet. J'ai des problèmes d'accès à la propriété que je ne peux pas expliquer actuellement.


Les réponses ici, en utilisant spécifiquement Apache ReflextringtostringBuilder, ne sont pas une mauvaise idée: Stackoverflow.com/Questtions/603013/...


4 Réponses :


6
votes

Exemple:

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


3 commentaires

J'utilise déjà quelque chose de similaire à cela, mais je reçois beaucoup d'exceptions d'accès, même pour les choses qui sont des vals publics. Je suis confus à ce sujet, comme je m'attendais à ce que je m'attendais à ce que j'avais du code comme celui-ci: la classe FOO {VAL BAR = 3} que si j'ai appelé, à partir d'une autre classe, FOO.GETDECLaredfield ("bar"), l'objet de terrain résultant aurait le modificateur "public". J'utilise java.lang.reflect.MODIFIC.MODIFICE pour décoder les champs de modificateur et ne fonctionne pas. Ceci est en 2.7.7, je ne suis donc pas sûr que cela diffère de 2,8 à cet égard.


J'ai trouvé que val obj = champ.get (ceci); Val Str = obj.ASInstance de [string] ne fonctionne pas. Bizarre


Pour ceux qui utilisent Scala 2.10, remplacez Deepmkstring avec mkstring



2
votes

Scala ne génère aucun domaine public. Ils vont tous être privés. Les méthodes accessoires sont ce qui sera public, réfléchir à ceux-ci. Compte tenu d'une classe comme: xxx

Le bytecode généré ressemble à: xxx


1 commentaires

Je crois qu'une fois il y a longtemps, j'avais l'habitude de le savoir. Merci!



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

1 commentaires

J'aurais dû mentionner que je suis coincé avec 2.7.7 pour l'instant. Je n'étais pas sûr de certaines des nouvelles constructions 2.8 que vous avez utilisées dans votre code et que vous avez terminé avec une exception Null Pointer. Cela ressemble à un moyen assez propre de le faire, alors la réponse va à vous!



4
votes

Savez-vous que les classes de cas Scala obtiennent ces méthodes générées par compilateur:

  • TOSTRING (): String
  • est égal (autre: n'importe quel): boolean
  • hashcode: int

    Ils obtiennent également des objets de compagnon pour des constructeurs "nouveaux" et une correspondance de modèle "

    Le TOSTRING ()


1 commentaires

Oui, les classes de cas sont magiques. Malheureusement, je ne les utilise pas ici, pour diverses raisons. J'utilise toutes les fonctionnalités que vous avez énumérées dans d'autres endroits. Je serai sûr que mon trait de tostring peut gérer des classes de cas à l'avenir. En outre, vous vous voyez sur IRC hier soir, petit monde.