1

I have the following function that should map from one data class to another but it is not working:

private fun ClassA.toClassB() = ClassB(

  text = text,
  meta = meta,
  message = message)

ClassB is underlined in red with the error None of the following functions can be called with the arguments supplied.

However, just mapping the attribute text works fine:

private fun ClassA.toClassB() = ClassB(

  text = text)

So I guess the upper code doesn't work because of the enum attributes.

ClassA and ClassB look exactly the same, like this:

data class ClassB(
  @JsonProperty("text")
  val text: String,
  @JsonProperty("meta")
  val meta: Meta?,
  @JsonProperty("message")
  val message: Message?,
) {

  constructor(text: String) : this(
    text = text,
    meta = null,
    message = null,
  )


  enum class Meta {
    ONE, TWO, THREE
  }

  enum class Message {
    ONE, TWO
  }}

So how can I map data classes that use enum attributes?

11
  • Try removing data keyword Commented Sep 15, 2021 at 6:45
  • @DeePanShu I did and it's still throwing that error. Commented Sep 15, 2021 at 6:48
  • check if there is a class name same as ClassB in your whole project Commented Sep 15, 2021 at 6:53
  • @DeePanShu Yes, it's java class however (ClassB.java) Commented Sep 15, 2021 at 6:59
  • Please, try to change the name or exclude java class or include your class package Commented Sep 15, 2021 at 7:02

2 Answers 2

2

Your Error

"ClassA and ClassB look exactly the same" -> This results in "duplicate" enums:

Meta & Message exist both in ClassA and in ClassB

This leads to the following error:

private fun ClassA.toClassB() = ClassB(
  text = text, // ok
  meta = meta, // expected: ClassB.Meta, provided: ClassA.Meta
  message = message // expected: ClassB.Message, provided: ClassA.Message
)

You now have two options:


You only really need one enum

  • move one somewhere toplevel
  • remove the other one
  • make sure you fix your imports

You actually need two different enums

e.g.

// ClassA
enum class Meta {
    ONE, TWO, THREE
  }

// ClassB
enum class Meta {
    UNO, DOS, TRES
  }

Then i suggest you create a function like:

private fun ClassA.Meta.toClassBMeta() =
    when(this){
        ClassA.Meta.ONE -> ClassB.Meta.UNO,
        ClassA.Meta.TWO -> ClassB.Meta.DOS,
        ClassA.Meta.THREE -> ClassB.Meta.TRES,
    }

Use it in your mapper:

private fun ClassA.toClassB() = ClassB(
  text = text,
  meta = meta.toClassBMeta(),
  message = message.toClassBMessage())
Sign up to request clarification or add additional context in comments.

2 Comments

Hey, thank you for your answer. Can you help me with how to write the function private fun ClassA.Meta.toClassBMeta(): ClassB.Meta to map the enums? I'm not sure how to go about doing that.
Thank you, I appreciate it very much and learned cool new syntax there :)
1

I think your problem lies in the way you have defined your enums. It seems in your code snippet that you defined them within the scope of ClassB. You stated the contents of both ClassA and ClassB are identical, so I'm assuming you defined the enums in both. This is where your problem lies.

When compiling, every object creates a sort of "signature" as a unique identifier, so that the compiler knows ClassA from ClassB. Since you define the enums in both classes, they too get unique signatures.

So, instead of having 2 signatures for the enums, you have 4. The compiler tries cloning it (which is essentially what you're doing), but fails because it tries to directly map to an enum with a different signature.

Quite literally "same same, but different, but still same".

What you should try to do is define the enums outside of the classes.

namespace com.path1
{
  enum class Meta {
    ONE,
    TWO,
    THREE
  }

  enum class Message {
    ONE,
    TWO
  }

  data class ClassA(
    @JsonProperty("text")
      val text: String,
    @JsonProperty("meta")
      val meta: Meta? = null,
    @JsonProperty("message")
      val message: Message? = null
  ) {}
}

namespace com.path2 {
  import com.path1.Meta
  import com.path2.Message

  data class ClassB(
    @JsonProperty("text")
      val text: String,
    @JsonProperty("meta")
      val meta: Meta? = null,
    @JsonProperty("message")
      val message: Message? = null
  ) {}
}

Above code will compile only 2 signatures for the enums. Assuming the contents of your classes remain identical, they will now both use the same signature for the enum classes.

Also notice how I deleted your constructor. If you want every parameter to be instantiable by default, omit defining a constructor completely, as the way you defined the properties in the class form a constructor already.

9 Comments

The secondary constructor defined like this most likely won't compile because of signature conflict with the primary one. Why declare it at all? Also toClassB() is user-defined, it's in the question.
@Joffrey Good point, will update my answer
You can do that, both enum properties are nullable, so just default them to null in the property declaration. Like so: val meta: Meta? = null, val message: Message? = null. That way, when instantiating in code like val classB = ClassB(text = "test"), all other properties will default to null. See edited answer
I'm not very well-versed in Kotlin enums yet, but a quick search came up with this result: geeksforgeeks.org/enum-classes-in-kotlin, perhaps that might help?
Also, here is Kotlin's own specification on enums: kotlinlang.org/api/latest/jvm/stdlib/kotlin/-enum
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.