Reputation: 13258
I have this Scala code running on an Android device:
// create Map
val myMap1 = new HashMap[Int, String]()
myMap1.put(1, "a")
// write it to file
val outStream = context.openFileOutput("test.txt", Context.MODE_PRIVATE)
val ostream = new ObjectOutputStream(outStream)
ostream.writeObject(myMap1)
ostream.close
// read from file
val inStream = context.openFileInput("test.txt")
val istream = new ObjectInputStream(inStream)
val myMap2 = (istream.readObject).asInstanceOf[HashMap[Int, String]]
istream.close
// java.lang.NullPointerException accessing myMap2
if (myMap2.contains(1)) { println("yes") } else { println("no") }
I create a mutable.HashMap and write it to a file, read it and then the HashMap is null. Why is myMap2
null and do not have any contents? Below is a screenshot of the debug session.
Full stacktrace:
java.lang.NullPointerException
at scala.collection.mutable.HashTable$class.index(HashTable.scala:353)
at scala.collection.mutable.HashMap.index(HashMap.scala:39)
at scala.collection.mutable.HashTable$class.findEntry(HashTable.scala:130)
at scala.collection.mutable.HashMap.findEntry(HashMap.scala:39)
at scala.collection.mutable.HashMap.contains(HashMap.scala:60)
at com.test.mytest.bean.MyItem$.read(MyItem.scala:74)
at com.test.mytest.bean.MyItem$.add(MyItem.scala:93)
at com.test.mytest.frag.MyFragment.onClick(MyFragment.scala:114)
at android.view.View.performClick(View.java:4084)
at android.view.View$PerformClick.run(View.java:16966)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Upvotes: 2
Views: 974
Reputation: 32719
The reason is that HashTable
contains a protected field table
that is declared as "transient":
@transient protected var table: Array[HashEntry[A, Entry]]
So it is never written to the file, and when deserializing keeps its default null
value.
However, HashMap
defines readObject
and writeObject
to explictly handle the serialization (I have not taken the time to dive in the whole code but most certainly this is supposed to handle the reading/writing of the table
field, among others) so it appears that it should work anyway.
Now, let me make an educated guess here: you are on Android, so most certainly you are using a tool like proguard to remove all the unneeded code (and shrink the code you do need via renamings). The problem then is that readObject
and writeObject
are private, so depending on your proguard configuration, proguard might assume that the methods are never accessed and thus remove them altogether. Thus at runtime, only the standard serialization is used and the custom serialization that is supposed to handle table
never happens.
What you should do is modify your proguard configuration so as to keep the readObject
and writeObject
methods. The standard proguard documentation even has an example of this here (search for "readObject"): http://proguard.sourceforge.net/index.html#manual/examples.html
Upvotes: 3