KotlinPoet: Add function to existing class
Asked Answered
T

1

0

I want to build an annotation processor that generates a public "non-mutable class" getter function of a private "mutable class" field (e.g. returning a LiveData version of a MutableLiveData field).

What I want to write:

class MyClass {  
    @WithGetNonMutable  
    private val popup: MutableLiveData<PopupTO?> = MutableLiveData()  
}

What I want to generate

class MyClass {  
    private val popup: MutableLiveData<PopupTO?> = MutableLiveData()  
    fun getPopup(): LiveData<PopupTO?> = popup  
}

Generating the function with the correct return type is no problem:

val liveDataType = ClassName("android.arch.lifecycle", "LiveData")
val returnType = liveDataType.parameterizedBy(genericDataType)

val function = FunSpec.builder("get${element.simpleName}")
                .addModifiers(KModifier.PUBLIC)
                .addStatement("return ${element.simpleName}")
                .returns(returnType)
                .build()

The problem is that the variable (popup) is private - so to access it my generated function also needs to be part of that class (it can't be a simple extension function in a new file). The KotlinPoet example all write to new files - but there's no way to access the private field (or is there?) so I'd need to write the function in the actual class file? How can I achieve this?

Tawanda answered 16/12, 2018 at 20:21 Comment(2)
KotlinPoet(and annotation processing in general)is about generating new code and not changing existing code. So you can't really modify that class.Callaghan
You might look into writing a compiler plugin that would add the getter directly in bytecode. Anyway, KotlinPoet is just a helper for generating code, it doesn't know or care about the mechanics of annotation processing or compilation.Cambodia
E
1

Annotation Processors cannot modify existing code, they can only generate new code.


That said, you could maybe modify your approach and generate an extension function instead of a member function.

  1. Keep your MyClass (with private modifier changed to internal):
class MyClass {  
    @WithGetNonMutable  
    internal val popup: MutableLiveData<PopupTO?> = MutableLiveData()  
}
  1. Generate a new file (within the same package), with the following contents:
fun MyClass.getPopup(): LiveData<PopupTO?> = this.popup  

If you completely can't modify the MyClass (you can't change private to internal), you can (it's not that elegant, but you can do it):

In the generated extension function use Reflection to access a private field.

Explain answered 11/1, 2019 at 16:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.