Überraschung programmiert mit asInstanceOf


Wer ein wenig Scala geschrieben hat, merkt schnell, dass man – insbesondere dank Pattern Matching – viel weniger casten muss als in Java. Die Methode zur Typkonvertierung hat absichtlich einen abschreckenden Namen bekommen: asInstanceOf. Und das zu Recht, denn Casts in Scala sind nicht sicherer als in Java.

Allerdings könnte man meinen, man sei sicher, nachdem der Cast „gelungen“ ist. Natürlich ist das schon in Java nicht der Fall, wenn man z.B. ein Objekt zu List<String> castet, das in Wahrheit eine List<Integer> ist. Der Cast gelingt, aber beim Zugriff auf ein Listen-Element gibt es einen lauten Knall. Aber wer Type Erasure in Java verstanden hat, sollte sich über dieses Verhalten eigentlich nicht wundern, denn wenn zur Laufzeit kein Parametertyp mehr existiert, kann man ihn auch nicht testen.

Neulich bin ich auf Stackoverflow auf ein anderes Problem gestoßen, dass es nur in Scala gibt: Man kann an eine Klasse mit asInstanceOf ungestraft andere Klassen oder Traits „anhexen“, ohne dass das beanstandet wird – natürlich nur, solange man nicht auf irgendwelche Member zugreifen möchte. Dabei kann die verhexte Klasse sogar final sein, wie z.B. Int:

trait Funny{ 
   def test { println("funny!") } 
}

def doubleIt(z: Int with Funny):Int = 2*z

val x = 5.asInstanceOf[Int with Funny] 

print(doubleIt(x)) // 10

print(x.test) // ClassCastException

Wir definieren hier ein Trait namens Funny, und eine Funktion namens doubleIt, die ein Int with Funny erwartet. Natürlich können wir diesen Typ nie wirklich konstruieren, denn Int ist final. Der Versuch, doubleIt mit einem normalen Int aufzurufen, würde übrigens schon beim Compilieren scheitern.

Als nächstes kreieren wir mit asInstanceOf ein Frankenstein-Monster namens x, das den gewünschten Typ besitzt, und ES IST LEBENDIG, ES LEBT!!!… Ähm, ich meine, man kann damit problemlos doubleIt aufrufen, und erhält das gewünschte Ergebnis.

Die letzte Zeile zeigt die dunkle Seite unseres Monsters: Es ist in Wirklichkeit gar nicht Funny, und wenn wir eine Methode des Traits aufzurufen versuchen, macht es puff und explodiert in einer ClassCastException.

Das gezeigte Verhalten ist natürlich ziemlich gefährlich, und man fragt sich, warum asInstanceOf-Aufrufe nicht testen, was da eigentlich für Typen von ihnen verlangt werden. Prinzipiell wäre das mit isInstanceOf möglich, aber vermutlich wären derartige Laufzeittests ziemliche Performance-Killer.

Was lernen wir daraus? Zum einen, dass man mit asInstanceOf den Typchecker wirklich „abschaltet“ und allen möglichen Unsinn anstellen kann – etwa Klassen vorzugaukeln, die es prinzipiell nicht geben kann. Zum anderen, dass man um asInstanceOf einen großen Bogen machen sollte, wenn man es nicht unbedingt braucht und nicht ganz genau weiß was man tut.

Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s