Search code examples
javaserializationlinkedhashmapobjectoutputstream

Java LinkedHashMap converted to Object is serialized as another class?


I am trying to serialize a LinkedHashMap to be written to an ObjectOutputStream, but I've encountered something strange. The ObjectOutputStream, writes the LinkedHashMap converted to an Object, however it is not a LinkedHashMap, it is writted as my class called SQLRow?!

Host.instance.getLogger().info("Sending back player data...");
final LinkedHashMap<String, NetworkedSQLObject> hm = MirageSQLTables.players.getByPrimaryKey(rest.readUTF()).getAsSocketEligibleMap();
Host.instance.getLogger().info("Linked Hash Map class name: " + hm.getClass().getName());
c.sendMessage(new SQLObjectResponsePacket(pID, (Object)hm));

NOTE: The function getByPrimaryKey() is a custom SQL function that returns my custom class, SQLRow.

output of code above:


14:37:03 [INFO] [Host] Sending back player data...
14:37:03 [INFO] [Host] Getting col: id
14:37:03 [INFO] [Host] Getting col: name
14:37:03 [INFO] [Host] Getting col: date_joined
14:37:03 [INFO] [Host] Getting col: rank
14:37:03 [INFO] [Host] Getting col: moderator
14:37:03 [INFO] [Host] adding NetworkedSQLObject: id:TEXT -> 2
14:37:03 [INFO] [Host] adding NetworkedSQLObject: name:TEXT -> KrispWasntTaken
14:37:03 [INFO] [Host] adding NetworkedSQLObject: date_joined:TIMESTAMP -> 2023-06-14 18:19:15.0
14:37:03 [INFO] [Host] adding NetworkedSQLObject: rank:INT -> 0
14:37:03 [INFO] [Host] adding NetworkedSQLObject: moderator:BIT -> true
14:37:03 [INFO] [Host] Linked Hash Map class name: mirage.database.sql.SQLRow$1 <---- HERE!

It says the same thing in the NetworkedSQLObject constructor. The function getAsSocketEligibleMap() is INSIDE the SQLRow class, but I am really confused as it has no ties to the class and returns a fresh new LinkedHashMap, not an instance of a SQLRow...

getAsSocketEligibleMap() code:


public LinkedHashMap<String, NetworkedSQLObject> getAsSocketEligibleMap(){
        return new LinkedHashMap<>(){{
            Set<Map.Entry<String, SQLObject>> es = data.entrySet();

            Iterator<Map.Entry<String, SQLObject>> i = es.iterator();

            while(i.hasNext()){
                Map.Entry<String, SQLObject> entry = i.next();
                Host.instance.getLogger().info("adding NetworkedSQLObject: " + entry.getKey() + ':' + entry.getValue().getType() + " -> " + entry.getValue().getValue());
                put(entry.getKey(), new NetworkedSQLObject(entry.getValue()));
            }
        }};
    }

Any help is appreciated...


Solution

  • You have used the double brace initialization pattern in your class, which is generally not a desirable pattern (the double {{}}). What that does is effectively define a new, inner class. So you are not returning an actual LinkedHashMap, you are returning an anonymous inner class which extends from LinkedHashMap. Since it is an inner class, it also retains a reference to the outer class, in this case, your SQLRow class. Simply removing the double brace initialization pattern should solve your problem. (this is one of the many reasons the pattern should be avoided).