Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement java.io.Serializable for some of the classes #373

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

dkhalanskyjb
Copy link
Collaborator

Implement java.io.Serializable for

  • Instant
  • LocalDate
  • LocalTime
  • LocalDateTime
  • UtcOffset

TimeZone is not Serializable because its behavior is system-dependent. We can make it java.io.Serializable later if there is demand.

We are using string representations instead of relying on Java's entities being java.io.Serializable so that we have more freedom to change our implementation later.

Fixes #143

@Moozart
Copy link

Moozart commented Apr 9, 2024

Can you please merge this commit if possible? @ilya-g

Copy link
Member

@ilya-g ilya-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For better control over serialized representation I would suggest using writeReplace/readResolve with an Externalizable container.

@@ -111,6 +113,25 @@ public actual class Instant internal constructor(internal val value: jtInstant)

internal actual val MIN: Instant = Instant(jtInstant.MIN)
internal actual val MAX: Instant = Instant(jtInstant.MAX)

@JvmStatic
private val serialVersionUID: Long = 1L
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually use const val for that.

@dkhalanskyjb
Copy link
Collaborator Author

I don't understand the idea. Do you mean that LocalTime should be Externalizable?

From the docs (https://docs.oracle.com/javase%2F7%2Fdocs%2Fapi%2F%2F/java/io/Externalizable.html):

When an Externalizable object is reconstructed, an instance is created using the public no-arg constructor

But we don't have public no-arg constructors. I guess we could do something like class LocalTime { @PublishedApi internal constructor() }, which would make it a public constructor for the JVM, and trick Kotlin somehow to avoid initializing val value: java.time.LocalTime in that constructor, but is it worth it to make such intrusive changes just to support Java serialization?

@akhbulatov
Copy link

A very necessary implementation on Android to be able to pass LocalDate/LocalDateTime objects between screens

@dkhalanskyjb
Copy link
Collaborator Author

@akhbulatov, it's not "necessary", just convenient: see #143 (comment)

Implement java.io.Serializable for
* Instant
* LocalDate
* LocalTime
* LocalDateTime
* UtcOffset

TimeZone is not `Serializable` because its behavior is
system-dependent. We can make it `java.io.Serializable` later
if there is demand.

We are using string representations instead of relying on Java's
entities being `java.io.Serializable` so that we have more freedom
to change our implementation later.

Fixes #143
@wkornewald
Copy link
Contributor

Do you have an estimate when this will get merged? It's a pretty huge inconvenience for us to wrap Instant because we want to export an SDK and the wrapping shouldn't be exposed to our users. It seems to be impossible without writing huge amounts of boilerplate.

Copy link
Member

@ilya-g ilya-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at how the list and set builders are serialized in stdlib. I believe we'd better have something similar here.
If you want, I can take over this branch and implement it accordingly.

}

private fun writeObject(oStream: java.io.ObjectOutputStream) {
oStream.defaultWriteObject()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this would write all object fields first.

@dkhalanskyjb
Copy link
Collaborator Author

Do you mean https://github.com/JetBrains/kotlin/blob/3be0aa15f714726e4872c09b35f789652c46876d/libraries/stdlib/jvm/src/kotlin/collections/builders/ListBuilder.kt#L674? Ok, I think I got the idea, thanks. This does look better than reflection.

It's not important to me who does this.

import kotlinx.datetime.*
import java.io.*

internal class SerializedValue(var typeTag: Int, var value: Any?) : Externalizable {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we can place it in kotlinx.datetime package and name it shortly, e.g. Ser, to shave off some bytes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean, shave off some bytes from the serialized representation? I'm okay with that, as long as it still stays in the internal/ directory.

Does it make sense to mark this with @PublishedApi to signal that it's an incompatible change to move/rename this class?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to mark this with @PublishedApi to signal that it's an incompatible change to move/rename this class?

Yes, I can do that

import kotlinx.datetime.*
import java.io.*

internal class SerializedValue(var typeTag: Int, var value: Any?) : Externalizable {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these values be private?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They can, but does it makes a difference for an internal class? Or you mean in order not to generate getters/setters?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, for the latter.

import kotlinx.datetime.*
import java.io.*

internal class SerializedValue(var typeTag: Int, var value: Any?) : Externalizable {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean, shave off some bytes from the serialized representation? I'm okay with that, as long as it still stays in the internal/ directory.

Does it make sense to mark this with @PublishedApi to signal that it's an incompatible change to move/rename this class?

@dkhalanskyjb
Copy link
Collaborator Author

I think it's good to go, but can't approve a pull request that I myself opened.

@dkhalanskyjb
Copy link
Collaborator Author

The reason I'm not clicking the "merge" button is that we are going to move kotlinx.datetime.Instant to kotlin.time.Instant (#382), so I don't think it makes sense to make it java.io.Serializable. @ilya-g, WDYT about removing java.io.Serializable for Instant from this PR and merging the implementation for the rest of the classes?

@wkornewald
Copy link
Contributor

Does this mean the stdlib will get a nice solution for adding Serializable support from within KMP code? I've contributed something to Ktor which could possibly serve as a starting point for a simple helper API in the stdlib: ktorio/ktor#4421

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement java.io.Serializable on the JVM
5 participants