On Thu, 11 Dec 2025 04:45:41 GMT, Prasanta Sadhukhan <[email protected]>
wrote:
>> Issue is when JTable is in editing mode, it is not Serializable as it gives
>> exception
>>
>> java.io.NotSerializableException: java.lang.reflect.Constructor
>> at
>> java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1149) at
>> java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1502)
>> at
>> java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1467)
>> at
>> java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1385)
>> at
>> java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1143) at
>> java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1502)
>> .......
>>
>>
>> It is caused by creation of `GenericEditor` class which uses a
>> non-serializable Constructor field.
>> This is fixed by making the field transient..
>> Also, `editorRemover` field is made transient as it is object of
>> `CellEditorRemover` class which is not Serializable..
>
> Prasanta Sadhukhan has updated the pull request incrementally with one
> additional commit since the last revision:
>
> GenericEditor serialization fix
As I found, the `GenericEditor `is instantiated and stored as `Object `class in
`defaultEditorsByColumnClass `field. So, it seems when
`ObjectOutputStream.writeObject` is called, `GenericEditor `is getting
serialized as it implements Serializable. `defaultEditorsByColumnClass `is
transient so that field will be skipped when the parent JTable object is
serialized however, the items stored inside the Hashtable are still objects
that exist in memory. The issue is that the standard Java serialization
mechanism might still find and serialize the GenericEditor instances through
other, less obvious references that Swing creates i.e The transient keyword
hides your initial map reference, but doesn't prevent the object from being
serialized if reached via other paths.`
defaultEditorsByColumnClass` uses a `UIDefaults.LazyValue`. This lazy value
doesn't create the GenericEditor immediately; it stores a function (a lambda
expression) that creates one when `createValue(t)` is called.
The key point is that JTable or the Swing look-and-feel code eventually calls
this lazy value to get the actual GenericEditor instance when it needs a
default editor. The created GenericEditor instance is then likely stored in an
another internal, non-transient field within the JTable or a related UI
delegate object.
When you serialize a JTable, Swing's serialization process involves
JTable itself implements Serializable.
It has an internal field for a tableHeader.
It has a ui field (the UI delegate, e.g., BasicTableUI), which also manages
aspects of the table's appearance and behavior, including editors.
When we initialize the JTable and it prepares its default editors, it creates
instances of those editors. The JTable keeps a strong, non-transient reference
to the actual GenericEditor instance via its internal machinery (likely through
the TableCellEditor storage mechanism).
Even if the custom hashtable reference is transient, the built-in Swing fields
that now hold the reference to the GenericEditor instance are not transient and
participate in the standard serialization process
I tried having GenericEditor implement Externalizable to have control of the
GenericEditor serialization process so that GenericEditor doesn't participate
in the serialization process and override writeExternal, readExternal.
Also, readObject() seems to install UI again which is done anyway in
writeObject (as is usually done for other classes) which is also removed.
Also, In writeObject(), cell editing is stopped as all editor related fields
are transient but something still retains the reference to JTextField..
-------------
PR Comment: https://git.openjdk.org/jdk/pull/28627#issuecomment-3653588080