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

Reply via email to