Map abstract type in Kotlin with MapStruct - kotlin

I'm trying to map an abstract class with Mapstruct in Kotlin, but getting the following error:
AnimalMapper.java: error: The return type AnimalOutput is an abstract class or interface. Provide a non abstract / non interface result type or a factory method.
My implementation:
#Mapper(componentModel = "jsr330")
interface AnimalMapper {
fun mapToDogOutput(dogInput: DogInput): DogOutput
fun mapToCatOutput(catInput: CatInput): CatOutput
fun mapToAnimalOutput(animalInput: AnimalInput): AnimalOutput {
when (animalInput) {
is DogInput -> mapToDogOutput(animalInput)
is CatInput -> mapToCatOutput(animalInput)
}
throw RuntimeException("Unsupported animal type");
}
}
sealed class AnimalInput {
abstract val name: String
}
data class CatInput(
override val name: String,
val catProperty: Int,
) : AnimalInput()
data class DogInput(
override val name: String,
val dogProperty: Float,
) : AnimalInput()
sealed class AnimalOutput {
abstract val name: String
}
data class CatOutput(
override val name: String,
val catProperty: Int,
) : AnimalOutput()
data class DogOutput(
override val name: String,
val dogProperty: Float,
) : AnimalOutput()
In my old java project, this corresponding implementation works as expected:
#Mapper(componentModel = "jsr330")
interface AnimalMapper {
DogOutput mapToDogOutput(DogInput dogInput);
CatOutput mapToCatOutput(CatInput catInput);
default AnimalOutput mapToAnimalOutput(AnimalInput animalInput) {
if (animalInput instanceof DogInput) {
return mapToDogOutput((DogInput) animalInput);
}
if (animalInput instanceof CatInput) {
return mapToCatOutput((CatInput) animalInput);
}
throw new RuntimeException("Unsupported animal type");
}
}
public abstract class AnimalInput {
public String name;
}
public abstract class CatInput extends AnimalInput {
public String name;
public int catProperty;
}
public abstract class DogInput extends AnimalInput {
public String name;
public float dogProperty;
}
public abstract class AnimalOutput {
public String name;
}
public abstract class CatOutput extends AnimalOutput {
public String name;
public int catProperty;
}
public abstract class DogOutput extends AnimalOutput {
public String name;
public float dogProperty;
}
I'm using the following dependency versions:
mapstructVersion: 1.4.2.Final
kotlinVersion: 1.5.21 (jvmTarget 16)
Anyone have an idea, how to fix this issue in my Kotlin project?

The reason why this is not working in Kotlin is due to the fact that the generated code by Kotlin marks the mapToAnimalOutput method as an abstract method.
You'll need to use #JvmDefault on that method in order for Kotlin to generate the correct modifiers for the method.

Related

How to initialize a variable of an abstract class from a child class

I have an abstract class:
inner abstract class Base {
fun Buscar(): Int {
// Do something with a object called Q
}
}
And two secondary class that I need to initialize Q with certain class
inner class BFS(): Base() {
constructor(): this() {
Q = Cola()
}
}
inner class DFS(): Base() {
constructor(): this() {
Q = Pila()
}
}
How can I do that?
--- EDIT ---
Pila() and Cola() are classes from an abstract class called Secuencia:
abstract class Secuencia<T> { ... }
public class Pila<T> : Secuencia<T>() { ... }
public class Cola<T> : Secuencia<T>() { ... }
You just need to make Q abstract and initialise it in your class, e.g.
sealed class Algo(val message: String)
class Cola : Algo("cola")
class Pila : Algo("pila")
abstract class Base {
// all concrete classes need to define a value for 'q'
abstract val q: Algo
fun buscar(): Int {
// you can reference q here since a concrete class will have initialised it
println(q.message)
return 1
}
}
class BFS(): Base() {
// since q is abstract it needs to be overridden in a concrete class
override val q = Cola()
}
class DFS(): Base() {
override val q = Pila()
}
fun main() {
BFS().buscar()
DFS().buscar()
}
>> cola
>> pila

in kotlin, how to access protected static member in parent class from sub class

It is code worked in java but after convert to kotlin it does not compile.
Having a base class which has some defines as static protected member in the companion object:
abstract class ParentClass {
companion object {
#JvmField
final protected val SERVICE_TYPE_A = "the_service_type_a"
}
}
and the child class:
class ChildClass: ParentClass {
public override fun getServiceType(): String {
return SERVICE_TYPE_A. //<== got compile error
}
}
it does not compile.
how to access a parent class static protected member from subclass?
You need to use #JvmStatic instead as follows:
abstract class ParentClass {
companion object {
#JvmStatic
protected val SERVICE_TYPE_A = "the_service_type_a"
}
abstract fun getServiceType(): String
}
The final keyword in SERVICE_TYPE_A is redundant since everything is final by default in Kotlin. This also mean that if you want ParentClass to be extended, then you need to explicitly define it as open.
Then your ChildClass would look as follows:
class ChildClass: ParentClass() {
override fun getServiceType(): String {
return SERVICE_TYPE_A
}
}

how do you declare static property in kotlin?

public class Common {
public static ModelPengguna currentModelPengguna;
}
public class Common {
companion object {
val currentModelPengguna: ModelPengguna = ModelPengguna()
}
}
or if the object is static and you want it as Singleton
you can use
object Common {
val currentModelPengguna: ModelPengguna = ModelPengguna()
}
a static property in kotlin is introduced by the companion object further reading:
https://kotlinlang.org/docs/reference/object-declarations.html#companion-objects

Class is not abstract and does not implement abstract base class member

I'm confused by this Kotlin error associated with providing an implementation for an abstract class that has been imported from a maven package.
I have a maven library that is written in Kotlin and exposes an abstract class called APIGatewayRequestHandler. In my app that imports the library, I provide an implementation of the abstract class:
class GetWelcomeMessageHandler : APIGatewayRequestHandler<WelcomeMessage>()
fun handleAPIGatewayRequest(input: com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent, context: com.amazonaws.services.lambda.runtime.Context?): WelcomeMessage {
return WelcomeMessage()
}
}
The decompiled abstract class from the library looks like this:
public abstract class APIGatewayRequestHandler<T> public constructor() : com.amazonaws.services.lambda.runtime.RequestHandler<com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent, T> {
public abstract fun handleAPIGatewayRequest(input: com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent, context: com.amazonaws.services.lambda.runtime.Context?): T
public open fun handleRequest(input: com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent?, context: com.amazonaws.services.lambda.runtime.Context?): T {
/* compiled code */
}
}
I get the following error:
Class 'GetWelcomeMessageHandler' is not abstract and does not implement abstract base class member
public abstract fun handleAPIGatewayRequest(input: APIGatewayProxyRequestEvent, context: Context?): WelcomeMessage
I think you're just missing some override keywords. Namely, your abstract class should have it on the handleRequest method:
public abstract class APIGatewayRequestHandler<T> public constructor() : com.amazonaws.services.lambda.runtime.RequestHandler<com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent, T> {
public abstract fun handleAPIGatewayRequest(input: com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent, context: com.amazonaws.services.lambda.runtime.Context?): T
public override fun handleRequest(input: com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent?, context: com.amazonaws.services.lambda.runtime.Context?): T {
/* compiled code */
}
}
And then your GetWelcomeMessageHandler should have it on its handleAPIGatewayRequest method:
class GetWelcomeMessageHandler : APIGatewayRequestHandler<WelcomeMessage>() { // <-- This curly brace was also missing
override fun handleAPIGatewayRequest(input: com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent, context: com.amazonaws.services.lambda.runtime.Context?): WelcomeMessage {
return WelcomeMessage()
}
}

Type mismatch with generic parameter

I'm trying to run the following code, but I'm getting the following compiler error: Error:(12, 9) Type mismatch: inferred type is Child but Parent<Any> was expected.
abstract class Parent<T> {
abstract fun hi()
}
class Child: Parent<String>() {
override fun hi() {
println("Hi from child")
}
}
fun main(args: Array<String>) {
println("Hello, world!")
test(Child())
}
fun test(parent: Parent<Any>) {
parent.hi()
}
But the Java's equivalent, works as expected:
public class HelloWorld {
public static void main(String[] args) {
test(new Child());
}
public static void test(Parent object) {
object.hi();
}
}
abstract class Parent<T> {
public abstract void hi();
}
class Child extends Parent<String> {
public void hi() {
System.out.println("Hi from child");
}
}
What is wrong with the Kotlin code?
You want Parent<String> to be a subtype of Parent<Any>. Since String is a subtype of Any, what you're looking for is called covariance. You can mark the type parameter of Parent to behave this way with the out keyword:
abstract class Parent<out T> {
abstract fun hi()
}
See more about variance in the official documentation.
As for why the Java example works, #Carcigenicate already mentioned in the comment above that you're using raw types there, e.g. the parent parameter of your test function doesn't have a type parameter at all. You can achieve something similar with a star projection in Kotlin, if you really have to:
fun test(parent: Parent<*>) {
parent.hi()
}