0

Can anybody explain serialization problems in example like below. I have two the same (equals return true) map - one is initialized in standard way and second one initialized with double braces. Second one can't be serialized (throwed NotSerializableException).

Map<String, Object> m = new HashMap<String, Object>(){
    private static final long serialVersionUID = 1L;
{
    put("test", "String");
}};
Map<String, Object> m2 = new HashMap<String, Object>();
m2.put("test", "String");       

Assert.assertEquals(m, m2); // true
Assert.assertTrue(m.equals(m2)); // true
Assert.assertEquals(Utils.deserialize(Utils.serialize(m2)), m2); // ok
Assert.assertEquals(Utils.deserialize(Utils.serialize(m)), m); // java.io.NotSerializableException on serialize()

Utils class:

public class Utils {
    static public Object deserialize(byte[] b) throws IOException, ClassNotFoundException {
        ObjectInputStream ins = null;
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(b);
            ins = new ObjectInputStream(bais);
            return ins.readObject();
        } finally {
            if(ins != null) {
                ins.close();
            }
        }
    }

    static public byte[] serialize(Object o) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(o);
        oos.flush();
        oos.close();
        bos.close();
        return bos.toByteArray();
    }
}
  • What is the exact exception text? – Erwin Bolwidt Mar 06 '19 at 07:09
  • 2
    And try making the field static - since you're creating an anonymous inner class to initialise your map, you're pulling in your current object instance and it is not serializabke. The exact exception text should tell you *which* class isn't serializabke – Erwin Bolwidt Mar 06 '19 at 07:13
  • 2
    Note that *double brace initialization* is generally [considered an antipattern](https://stackoverflow.com/a/27521360/507738). – MC Emperor Mar 06 '19 at 07:15
  • You can check if their `hashCode`s are the same. If they aren't it won't likely `equal`. – deHaar Mar 06 '19 at 07:16

2 Answers2

1

Second one can't be serialized (throwed NotSerializableException).

This will be because you are initializing your map in a non-static method inside a non-serializable class.

Double-brace initialization is actually just defining an anonymous class with an instance initializer. Anonymous classes in non-static contexts capture a reference to the enclosing instance.

If that class isn't serializable, you can't serialize your anonymous class instance. It looks like this code is inside a unit test class; it would be very unusual for such a class to be serializable.

The easiest solution, honestly, is just to avoid double-brace initialization. It is a syntactic fudge that is too clever for its own good.

However, if you really insist on using it, you can simply do the initialization in a static method.

static Map<String, Object> doubleBrace() {
  return new HashMap<String, Object>(){
    private static final long serialVersionUID = 1L;
    {
      put("test", "String");
    }};
}

But this somewhat defeats the brevity of using double-brace initialization in the first place.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
0

In this demo, Map<String, Object> m = new HashMap<String, Object>(){ is an anonymous inner class, you can use System.out.println(m.getClass()) to check the class of m.

public class Utilt implements Serializable {
    private static final long serialVersionUID = -7271914225876022793L;

    @Test
    public void UtilTest() throws IOException, ClassNotFoundException {
        Map<String, Object> m = new HashMap<String, Object>(){
            private static final long serialVersionUID = 1L;
            {
                put("test", "String");
            }};
        Map<String, Object> m2 = new HashMap<String, Object>();
        m2.put("test", "String");

        Assert.assertEquals(m, m2); // true
        Assert.assertTrue(m.equals(m2)); // true
        Assert.assertEquals(Utils.deserialize(Utils.serialize(m2)), m2); // ok
        Assert.assertEquals(Utils.deserialize(Utils.serialize(m)), m);
    }
}
Yibo
  • 24
  • 2
  • Thanks for your answer! You may want to develop more on why creating an anonymous inner class makes it non-serializable. – Turtle Mar 06 '19 at 09:12