Scala weirdness
This one almost cost me a weeks work, and you are going to love it - maybe obvious to Scala practitioners - but definitely unintutive to someone who is just using Scala : like me.
Consider a class
class MyClass(private var field: Int) {
private def processField(value: Int) {
// For now, assume it is just setting it after some processing
this.field = value
}
}
Do a javap on it, and we get :
> $JAVA_HOME/bin/javap -p -cp . MyClass
Compiled from "MyClass.scala"
public class MyClass implements scala.ScalaObject {
private int field;
private int field();
private void field_$eq(int);
private void processField(int);
public MyClass(int);
}
Which is fine - our method processField exists : with same signature as we would expect if we had written it in java.
Now, let us add an object MyClass - which can directly invoke this processField. For completeness sake, I am including the modified .scala file below :
class MyClass(private var field: Int) {
private def processField(value: Int) {
// For now, assume it is just setting it after some processing
this.field = value
}
}
object MyClass {
def someMethod(inst: MyClass, value: Int) {
inst.processField(value)
}
}
Running scalac on this causes no errors - as expected : the object can invoke private methods on MyClass instance - as scala defines.
But now, there is a non trivial change to the generated class. Notice it below.
> $JAVA_HOME/bin/javap -p -cp . MyClass
Compiled from "MyClass.scala"
public class MyClass implements scala.ScalaObject {
private int field;
public static final void someMethod(MyClass, int);
private int field();
private void field_$eq(int);
public final void MyClass$$processField(int);
public MyClass(int);
}
Yep, the processField method is now NOT what we defined :
a) It has been renamed with namespace prefix.
b) It is now PUBLIC ! Yes, a private method has its access permission changed !
(b) is a very severe issue : and probably a bug IMO and (a) is what hit me as a issue
Now you ask, why all the fuss about all this ? After all, the names are private to scala and invocations to them work as expected.
Yes and no - when you are interfacing java with scala, things will fail.
And more importantly, when you are interfacing scala with java - explicitly or implicitly, things can fail.
The last is what happened to me - one word : SERIALIZATION.
I was invoking readObject/writeObject from the object - which, from a java background, is common practise when you have custom serialization (to invoke from a static method).
Here, it fails spectacularly - since the method signatures are royally mangled by scala.
So the dirty workaround ?
- Add all the actual logic within readObjectImpl and writeObjectImpl
- readObject and writeObject delegate to the Impl methods.
- object invocations are on the Impl and NOT on read/write Object
This preserves the method signature while ensuring things work.
I found this out the hard way - I hope there is an easier solution, but I did not find it.
In the hope that someone else might not trip over this issue and waste days, posting it here !
1 Comments:
Just saw this. Are you still doing the chess engine development?
Daneil Shroff
Post a Comment
<< Home