2010-01-06 13 views
6

Aby ułatwić introspekcję czasu debugowania na lekcjach, chciałbym utworzyć ogólną metodę toString w klasie bazowej dla przedmiotowych obiektów. Ponieważ nie jest to kod o krytycznym znaczeniu dla wydajności, chciałbym użyć Reflection do wydrukowania par nazwa/wartość pól ("x = 1, y = 2" itd.).Jak stworzyć przyzwoitą metodę toString() w scala za pomocą refleksji?

Czy istnieje prosty sposób na zrobienie tego? Wypróbowałem kilka potencjalnych rozwiązań i napotkałem problemy z dostępem do zabezpieczeń, itp.

Aby było jasne, metoda toString() w klasie bazowej powinna odbijać się refleksyjnie od publicznych znaków w klasach, które dziedziczą po niej, jak również wszelkie cechy, które są zmieszane w

+0

Czy nie powinna to być cecha? 'cecha ToString [A]'? – VonC

+0

To może być najczystszy sposób, aby to zrobić, nie zastanawiałem się jeszcze, jak to wpłynie na moją dziedziczność obiektu. Mam problemy z dostępem do usługi, których nie mogę obecnie wyjaśnić. –

+0

Odpowiedzi tutaj, w szczególności za pomocą Apache ReflectionToStringBuilder, nie są złym pomysłem: http://stackoverflow.com/questions/603013/dumping-a-java-objects-properties – ripper234

Odpowiedz

2
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") 
+0

Powinienem wspomnieć, że utknąłem z 2.7.7 na teraz. Nie miałem pewności co do niektórych nowych konstrukcji 2.8 użytych w kodzie, a skończyło się na wyjątku wskaźnika pustego. Wygląda to jednak na dość czysty sposób, więc odpowiedź trafia do Ciebie! –

+1

Zmontowałem przykład, aby był zgodny z 2.7.7. – retronym

6

. Przykład:

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

Używa Java Reflection API, więc nie zapomnij importowej java.lang.reflect._

Ponadto, może być konieczne do złapać IllegalAccessException na field.get (to) wywołuje w niektórych scenariuszach, ale to jest tylko jako punkt wyjścia.

+0

Używam już czegoś podobnego, ale Dostaję wiele wyjątków dostępu nawet w przypadku rzeczy, które są publiczne. Jestem mylić o tym, jak się spodziewałem, że gdybym miał kodu: class Foo {poprzeczkę val = 3 } że jeśli zadzwoniłem, z innej klasy, Foo.getDeclaredField ("bar"), wynikowy obiekt pola miałby modyfikator "public". Używam java.lang.reflect.Modifier do dekodowania pól modyfikujących i nie działa. Jest to w 2.7.7, więc nie jestem pewien, czy różni się on od 2.8 pod tym względem. –

+1

Znalazłem, że 'val obj = field.get (this); val str = obj.asInstanceOf [String] 'nie działa.Dziwne –

+1

To powinno zostać zaktualizowane do wersji 2.10 – ripper234

2

Scala nie generuje żadnych pól publicznych. Wszystkie będą prywatne. Metody dostępu są tym, co będzie publiczne, zastanowić się nad nimi. Biorąc pod uwagę klasę jak:

class A { 
    var x = 5 
} 

Wygenerowany kod bajtowy wygląda następująco:

private int x; 
public void x_$eq(int); 
public int x(); 
+0

Wierzę, że kiedyś to wiedziałem. Dzięki! –

4

jesteś świadomy klasy przypadków Scala Zdobądź te metody kompilator generowane:

  • toString (): String
  • równa się (inne: dowolne): Boolean
  • hashCode: Int

Dostają także towarzysz obiektów dla "New-less" konstruktorów i dopasowywania wzorców.

Wygenerowany toString() jest podobny do tego, który opisujesz.

+0

Tak, klasy przypadków są magiczne. Niestety nie używam ich tutaj, z różnych powodów. Używam jednak wszystkich funkcji wymienionych w innych miejscach. Będę pewien, że moja funkcja toString może obsługiwać klasy przypadków w przyszłości. Dobrze, widząc cię na IRC ostatniej nocy, mały świat. –

Powiązane problemy