https://kotlinlang.org logo
#getting-started
Title
# getting-started
g

Gustav Elmgren

02/11/2022, 11:15 AM
Hi everyone! Lets say I have the following structure:
Copy code
class Test {
    val firstValue: String
    val secondValue: Long
}

class TestWrapper {
    val firstValue: Wrapper<String>
    val secondValue: Wrapper<Long>
}
Would it be possible somehow to create a common interface/class for these two classes? For example, if it would be possible, using union types:
Copy code
interface InterfaceTest<T : Wrapper<T>, T>
But given that is not possible, is it some other way Kotlin can handle this?
e

ephemient

02/11/2022, 11:20 AM
Kotlin (like Java) has first-order generics only, no higher-order types, so not really.
j

Joffrey

02/11/2022, 11:30 AM
How would you use that common interface?
g

Gustav Elmgren

02/11/2022, 11:40 AM
The goal is to create the
Test
class based on
TestWrapper
via some reflections. And the purpose for the interface would just be for safety. That is that the value the wrapper wraps is the same type as the actual value.
Copy code
class Test {
    val firstValue: String
    val secondValue: Long
}

class TestWrapper {
    val firstValue: Wrapper<Long>
    val secondValue: Wrapper<Long>
}
Here the
TestWrapper#firstValue
wrapps a
Long
but the
Test#firstValue
is of type
String
j

Joffrey

02/11/2022, 11:43 AM
But if the
Test
class is generated from
TestWrapper
via reflection, why would the type not match?
g

Gustav Elmgren

02/11/2022, 11:44 AM
Hm... Today it is generated based on defined values, so the reflection is just setting the values, because in the end it works as a response DTO.
j

Joffrey

02/11/2022, 11:48 AM
Oh ok I think I misunderstood your previous statement. You're not creating the
Test
class from the
TestWrapper
class, you're creating
Test
instances via reflection from instances of the
TestWrapper
class, right?
g

Gustav Elmgren

02/11/2022, 11:49 AM
Oh, right. Yeah. Sorry. So I first create a
Test
instance (with just one required value), and then I use reflection to set all the values matched (by name and type) via reflection for the other values in the
Test
instance
j

Joffrey

02/11/2022, 11:52 AM
I see. And so your goal would be to make the function that does this reflection generic, and have a way to enforce at compile time that the type of the wrapper class is sort of "compatible" with the type of the DTO class, right? If that's the case, I don't think this is possible, unfortunately.
g

Gustav Elmgren

02/11/2022, 11:52 AM
Yepp, thats it!
e

ephemient

02/11/2022, 11:53 AM
I should correct my terminology: they're typically named higher-kinded types
👍 1
j

Joffrey

02/11/2022, 11:53 AM
You could also consider using code generation that generates one of the classes from the other, as well as methods that map wrappers to the corresponding DTOs
e

ephemient

02/11/2022, 11:53 AM
analogous to higher-order functions, but on types
g

Gustav Elmgren

02/11/2022, 11:55 AM
Oh... Right. Code generation would eliminate the code duplication, as well as ensure compatibility. That sounds like the way to go. Thank you!
e

ephemient

02/11/2022, 11:55 AM
yes I agree there will not be a safe way to ensure this type mapping within the language. custom codegen can enforce your own requirements such as this.
j

Johann Pardanaud

02/11/2022, 1:24 PM
the only solution I can think of is to create common interfaces for your values, you could do something like that :
Copy code
interface ValueProvider<T> {
  fun getValue(): T
}

class Value: ValueProvider {}
class ValueWrapper: ValueProvider {}

class Test {
    val firstValue: Value<String>
    val secondValue: Value<Long>
}

class TestWrapper {
    val firstValue: ValueWrapper<String>
    val secondValue: ValueWrapper<Long>
}
Then you can create a common interface for your
Test
and
TestWrapper
classes :
Copy code
interface InterfaceTest {
    val firstValue: ValueProvider<String>
    val secondValue: ValueProvider<Long>
}
j

Joffrey

02/11/2022, 1:27 PM
Yes, but I guess the initial idea was that the DTO's properties were not wrapped in any custom type like this, so creating another wrapper for this purpose defeats the purpose of the conversion (at least from what I understood of the @Gustav Elmgren's intention)
j

Johann Pardanaud

02/11/2022, 1:42 PM
ah yes, it may not be a good option
g

Gustav Elmgren

02/11/2022, 1:47 PM
That is correct, i kinda defeats the purpose as you said @Joffrey.
j

Joffrey

02/11/2022, 1:50 PM
It's worth mentioning as an option though, it might be ok for other people finding this thread 😉
m

Michael de Kaste

02/11/2022, 1:57 PM
I still think Kotlin needs union types. Sometimes on inferencing level we'll see
<Type1 | Type2>
and I'm kinda surprised that functionality just plain doesn't exist on declaring level.
3 Views