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

Add support for Kotlin Value Classes #2866

Closed
wants to merge 11 commits into from
Closed

Add support for Kotlin Value Classes #2866

wants to merge 11 commits into from

Conversation

mp911de
Copy link
Member

@mp911de mp911de commented Jun 22, 2023

We now support instantiation and mutation of types using Kotlin Value (@JvmInline) classes:

@JvmInline
value class MyValueClass(val id: String)

data class WithMyValueClass(val id: MyValueClass) { }
class WithMyValueClass {
  private final String id;

  WithMyValueClass(String id){…}
  // getters, setter, copy
}

Value classes are typically flattened into their value type (component type) if the type at the declaration site does not change. Nullable value classes will be boxed.

@JvmInline
value class MyNullableValue(val id: String?)

data class WithMyNullableValue(val id: MyNullableValue?) { }
class MyNullableValue {
  private final MyNullableValue id;

  WithMyValueClass(MyNullableValue id){…}
  // getters, setter, copy
}

Boxed values result in being recognized as entities.

The following components are affected by this change:

  • KotlinBeanInfoFactory
  • Reflective and class-generating EntityInstantiator
  • Reflective and class-generating PersistentPropertyAccessorFactory

Closes #2806

@mp911de mp911de linked an issue Jun 22, 2023 that may be closed by this pull request
@mp911de mp911de added the type: enhancement A general enhancement label Jun 22, 2023
@mp911de mp911de requested a review from christophstrobl June 22, 2023 13:33
@mp911de mp911de added the in: kotlin Kotlin support label Jun 28, 2023
@mp911de mp911de linked an issue Jun 28, 2023 that may be closed by this pull request
Switch to older ticket number.
@mp911de mp911de added status: duplicate A duplicate of another issue and removed status: duplicate A duplicate of another issue labels Jun 28, 2023
* @see JvmClassMappingKt
* @see ReflectJvmMapping
*/
public class KotlinBeanInfoFactory implements BeanInfoFactory, Ordered {
Copy link
Member

Choose a reason for hiding this comment

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

@sdeleuze what's your take on moving this one to core framework?

Copy link
Contributor

Choose a reason for hiding this comment

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

I have in my todo to have a broader look to value class support in Framework, I will share my feedback on this specific point after and comment here.

@odrotbohm
Copy link
Member

I wonder if more of the underlying Kotlin class analysis code should / would need to move to Spring Framework, so that the standard framework binding subsystem was enabled to populate these types as well?

christophstrobl pushed a commit that referenced this pull request Jul 7, 2023
This commit introduces support for Kotlin Value Classes which are designed for a more expressive domain model to make underlying concepts explicit. Spring Data can now read and write types that define properties using Value Classes.
The support covers reflection based instantiation of Kotlin inline class, nullability and defaulting permutations as well as value classes with generics.

Closes: #1947
Original Pull Request: #2866
christophstrobl added a commit that referenced this pull request Jul 7, 2023
Guard usage of KotlinReflectUtils with type presence check.
Extend tests to cover primitive arrays.
Move methods from KotlinValueUtils to KotlinReflectUtils.
Move copy value cache to KotlinCopyMethod.

Original Pull Request: #2866
@christophstrobl
Copy link
Member

merged to main development line.

christophstrobl added a commit that referenced this pull request Jul 7, 2023
KClass<?> kotlinClass = JvmClassMappingKt.getKotlinClass(type);

for (KCallable<?> member : kotlinClass.getMembers()) {
if (member instanceof KProperty<?> kp && isValueClass(kp.getReturnType())) {
Copy link

Choose a reason for hiding this comment

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

@mp911de Sorry for trespassing, but I believe this might work incorrectly in case there are unrelated properties in the class. For example:

@JvmInline
value class Z(val value: String)

data class D(val x: Int) {
    val unrelated1 = Z("1")

    val Any.unrelated2: Z get() = Z("2")
}

hasValueClassProperty will return true for the class D, but it probably should be false (if I understood call sites correctly).

I suppose you're interested in finding only if any property in the primary constructor has a value class type? For that you need to take only non-extension properties (i.e. KClass.memberProperties) whose names are equal to the parameters of the primary constructor.


if (kClass.isValue()) {

for (KCallable<?> member : kClass.getMembers()) {
Copy link

Choose a reason for hiding this comment

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

@mp911de Similarly to the above, this should use memberProperties, otherwise it'll fail on something like this:

@JvmInline
value class Z(val value: String) {
    val String.unrelated: String get() = this
}


if (kClass.isValue()) {

wrapperConstructor = kClass.getConstructors().iterator().next();
Copy link

Choose a reason for hiding this comment

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

@mp911de There's no guarantee that the first constructor is the primary one, nor that it has any parameters, so this can fail on something like this:

@JvmInline
value class Z(val value: String) {
    constructor() : this("Fail")
}

I recommend to use primaryConstructor instead.


KClass<?> nestedClass;

// bound flattening
Copy link

Choose a reason for hiding this comment

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

@mp911de You can use jvmErasure for this.

@mp911de mp911de added this to the 3.2 M1 (2023.1.0) milestone Jun 25, 2024
@mp911de mp911de deleted the issue/2806 branch November 4, 2024 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: kotlin Kotlin support type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for Kotlin Value Classes Add support for Kotlin Value Classes
5 participants