2013-03-07 3 views
2

Я этот Scala код работает на Android устройства:Scala - изменяемые HashMap -> запись в файл/чтение из файла -> HashMap нуль

// 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") } 

создать mutable.HashMap и записать его в файл , прочитайте его, а затем HashMap имеет значение NULL. Почему нет myMap2 null и не имеет содержания? Ниже приведен снимок экрана сеанса отладки.

Screenshot of Eclipse IDE - debug session

Полный 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) 
+0

myMap2 не является нулевым в вашем примере, так что это не то, что вызывает NPE. Можете ли вы опубликовать весь стек? – m0skit0

+0

Правильно, но tableSize и таблица пусты, и поэтому она выдает эту ошибку. tableSize должен быть 1 и table HashEntry, например 'myMap1''. – Tim

+0

Какая линия MyItem.scala: 74? – m0skit0

ответ

3

Причина заключается в том, что HashTable содержит защищенное поле table, который объявлен как "переходный":

@transient protected var table: Array[HashEntry[A, Entry]] 

Так что никогда не записывается в файла, и когда десериализация сохраняет значение по умолчанию null.

Однако HashMap определяет readObject и writeObject в Явно обрабатывать сериализации (я не взял время, чтобы нырнуть в весь код, но, безусловно, это, как предполагается обрабатывать чтения/записи в table области, среди других), так кажется, что он должен работать в любом случае.

Теперь позвольте мне дать обоснованное предположение: вы находитесь на Android, поэтому, безусловно, вы используете инструмент, например proguard, для удаления всего ненужного кода (и сжимаете код, который вам нужен, с помощью переименований). Тогда проблема заключается в том, что readObject и writeObject являются частными, поэтому в зависимости от конфигурации proguard proguard может предположить, что методы никогда не доступны и, таким образом, полностью удаляют их. Таким образом, во время выполнения используется только стандартная сериализация, и пользовательская сериализация, которая должна обрабатывать table, никогда не произойдет.

Что вам нужно сделать, это изменить конфигурацию proguard, чтобы поддерживать методы readObject и writeObject. Стандартная документация по proguard даже имеет пример этого здесь (поиск «readObject»): http://proguard.sourceforge.net/index.html#manual/examples.html

+0

Ты спас мой день! Он работает, добавляя линии в ProGuard! Но я не понимаю проблему, но мне нравится это делать.Я вызываю 'writeObject'' и' 'readObject'' из' 'ObjectOutputStream'' и' 'ObjectInputStream''. Что такое «стандартная сериализация»? Итак, HashTable переопределяет оба метода, но удалены ProGuard? – Tim

+0

И если sth. является «переходным», он не должен быть сериализуемым? Но почему я могу это сделать? – Tim

+1

Я не говорил о методах 'readObject' /' writeObject', которые вы вызываете (те, что в 'ObjectOutputStreamand'), но, скорее, методы thoses здесь: https://github.com/scala/scala/blob/v2 .10.0/SRC/библиотека/Скала/коллекция/изменяемые/HashMap.scala # L134. Короче говоря, добавление 'writeObject' в сериализуемый класс позволяет выполнять пользовательскую логику сериализации (то же самое для' readObject' и десериализации). HashMap делает именно это. См. Эту документацию: http://docs.oracle.com/javase/7/docs/platform/serialization/spec/output.html#861 –

Смежные вопросы