KotlinPoet: Add function to existing class - kotlin

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?

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.
Keep your MyClass (with private modifier changed to internal):
class MyClass {
#WithGetNonMutable
internal val popup: MutableLiveData<PopupTO?> = MutableLiveData()
}
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.

Related

Kotlin: Using method reference to a getter of another class

I have a class -
class ES {
var issue: SomeClass? = null
}
I need to access its getter in another class -
class CSS {
private val ref: Supplier<SomeClass?> = ES::issue
}
However this is not working. Its throws this error -
Type mismatch.
Required: Supplier<SomeClass?>
Found : KMutableProperty1<ES, SomeClass?>
Can someone tell me what am I doing wrong? I am actually in the process of converting java code to kotlin.
UPDATE
I need a static reference to the getter of the ES class, similar to JAVA, where we can do ->
Function<ES, SomeClass> ref = ES::getIssue;
In Kotlin, instead of using Supplier, you use functional syntax for the type. In this case, the equivalent of your Supplier<SomeClass?> would be () -> SomeClass? (assuming ES is an object since that's how you used it in your example code):
class CSS(/*...*/) {
private val ref: () -> SomeClass? = ES::issue
}
But if you have to use Supplier specifically so it can be easily used with Java code, you can wrap your getter reference in a Supplier implementation:
class CSS(/*...*/) {
private val ref: Supplier<SomeClass?> = Supplier(ES::issue)
}
Update
If you want the getter without a specific instance of the class, similar to Function<ES, SomeClass> in Java, then you need to make ES a parameter of the function.
Either
private val ref: (ES) -> SomeClass? = ES::issue
or
private val ref: ES.() -> SomeClass? = ES::issue
I don't believe there's a way to do this with Supplier, but I don't think you could do it in Java with Supplier either.

Kotlin - Extension for final class

Is it possible to create extension of final classes like String? Like in swift it is possible to add additional methods inside a extension of final class.
For an example - I would like to create a method in String extension which will tell me String have valid length for password.
val password : String = mEdtPassword!!.getText().toString()
// how to define haveValidLength method in extension
val isValid : Boolean = password.haveValidLength()
Note - That example is just for a sake to understand usability of extension, not a real scenario.
yes, you can. Kotin extension method provides the ability to extend a class with new functionality without having to inherit from the class or use any type of design pattern such as Decorator.
Below is an extension method for a String:
// v--- the extension method receiver type
fun String.at(value: Int) = this[value]
And the extension method code generated as Java below:
public static char at(String receiver, int value){
return receiver.charAt(value);
}
So an extension method in Kotlin is using delegation rather than inheritance.
Then you can calling an extension method like as its member function as below:
println("bar".at(1))//println 'a'
You also can write an extension method for the existing extension function, for example:
fun String.substring(value: Int): String = TODO()
// v--- throws exception rather than return "ar"
"bar".substring(1)
But you can't write an extension method for the existing member function, for example:
operator fun String.get(value: Int): Char = TODO()
// v--- return 'a' rather than throws an Exception
val second = "bar"[1]
Trying to add more detail, this answer might be helpful for someone.
Yes we can add additional methods to final classes like String. For an example I would like to add one method in String which will tell me that my String have valid number of characters for password or not.
So what I have to do is, I have ti create a below function which can be written in same class or at different separate class file.
fun String.hasValidPassword() : Boolean {
// Even no need to send string from outside, use 'this' for reference of a String
return !TextUtils.isEmpty(this) && this.length > 6
}
And now from anywhere call
val isValid : Boolean = password.haveValidLength()
Suggestion
If your application just has a single password validation, then there is no problem.
However, I don't suggest you write such a extension method hasValidPassword if the application has more than one validation. since the extension method is satically, you can't change your hasValidPassword in runtime. So if you want to change the validation in runtime, you should using a function instead, for example:
class PasswordRepository(private val validate:(String)->Boolean){
fun save(value:String){
if(validate(value)){
//TODO persist the password
}
}
}
val permitAll = PasswordRepository {true}
val denyAll = PasswordRepository {false}
permitAll.save("it will be persisted")
denyAll.save("it will not be persisted")
In other words, the extension method above violates Single Responsibility Principle, it does validation & string operations.
You can do that with extension functions in Kotlin. With extensions, you are able to add extra functionality to a class that you do or do not have access to; for example a legacy code base. In the example given in the Kotlin docs here, swap was added to MutableList<Int> which doesn't have swap originally. A this keyword is used that refers to the object that the swap functionality will operate on. In the example below, this refers to testList
val testList = mutableListOf(1, 2, 3)
testList.swap(0, 2)

is it possible to add a template to the getter/setter of a data class?

for example , I want to change all setters this way:
this.a = StringUtils.trim(a);
If it's a java bean, I can do this by modifying the code generating template of the ide. But Intellij seems not support to atomically add getter/setter for kotlin data class.
Is there a way to do this?
There is not a way to do this as of Kotlin 1.1.
A Kotlin data class, for the most part, is a class "to do nothing but hold data".
I think the closest you can get is to validate your data upon class initialization and make your data class properties read-only values. e.g.:
data class Data(val a: String) {
init {
require(a == a.trim())
}
}
The following won't throw an exception:
val a = Data("ab")
val b = a.copy(a = "abc")
While the following will:
val c = a.copy(a = "abc ")
It looks like if you declare the property as private, you can create your own getter/setters for accessing it. This example works for me.
fun main(args: Array<String>) {
var t = test("foo")
t.setHello("bar")
println(t)
}
data class test(private var hello: String) {
fun setHello(blah: String) {
this.hello = blah
}
}
But you will still have an issue when the property is passed in to the constructor. You will probably need to rethink how you are doing this, either declaring the field private and trimming it in the getter, or not using a data class for this instance.

Kotlin constructor parameter without getter

I have the following code:
class ViewHolder(itemView: View, val ui: ViewUI)
I don't want that the field ui have a getter, but kotlin is generating it by default since it is a val defined in the constructor.
In the properties and fields document I found that I can generate a private getter with the following code
var setterVisibility: String = "abc"
private set
But I can't find a way to do this in the constructor definition
You can make the entire property private in the constructor, that way it will only be accessible inside the class:
class ViewHolder(itemView: View, private val ui: ViewUI)

Extension fields in Kotlin

It's easy to write extension methods in Kotlin:
class A { }
class B {
fun A.newFunction() { ... }
}
But is there some way to create extension variable? Like:
class B {
var A.someCounter: Int = 0
}
You can create an extension property with overridden getter and setter:
var A.someProperty: Int
get() = /* return something */
set(value) { /* do something */ }
But you cannot create an extension property with a backing field because you cannot add a field to an existing class.
No - the documentation explains this:
Extensions do not actually modify classes they extend. By defining an extension, you do not insert new members into a class, but merely make new functions callable with the dot-notation on instances of this class.
and
Note that, since extensions do not actually insert members into classes, there’s no efficient way for an extension property to have a backing field. This is why initializers are not allowed for extension properties. Their behavior can only be defined by explicitly providing getters/setters.
Thinking about extension functions/properties as just syntactic sugar for calling a static function and passing in a value hopefully makes this clear.
However, if you really, really want to do something like this...
As stated above regarding efficiency, an additional backing field added directly to the class is the best way to store data non-derivable from existing non-private members from the class. However, if you don't control the implementation of the class and are dead-set on creating a new property that can store new data, it can be done in a way that is not abysmally inefficient by using separate external tables. Use a separate map that keys on object instances of this class with values that map directly to the value you want to add then define an extension getter and/or setter for this property which uses your external table to store the data associated with each instance.
val externalMap = mutableMapOf<ExistingClass, Int>()
var ExistingClass.newExtensionProperty : Int
get() = externalMap[this] ?: 0
set(value:Int) { externalMap[this] = value }
The additional map lookups will cost you - and you need to consider memory leaks, or using appropriately GC-aware types, but it does work.
There's no way to add extension properties with backing fields to classes, because extensions do not actually modify a class.
You can only define an extension property with custom getter (and setter for var) or a delegated property.
However, if you need to define an extension property which would behave as if it had a backing field, delegated properties come in handy.
The idea is to create a property delegate that would store the object-to-value mapping:
using the identity, not equals()/hashCode(), to actually store values for each object, like IdentityHashMap does;
not preventing the key objects from being garbage collected (using weak references), like WeakHashMap does.
Unfortunately, there is no WeakIdentityHashMap in JDK, so you have to implement your own (or take a complete implementation).
Then, based on this mapping you can create a delegate class satisfying the property delegates requirements. Here's an example non-thread-safe implementation:
class FieldProperty<R, T : Any>(
val initializer: (R) -> T = { throw IllegalStateException("Not initialized.") }
) {
private val map = WeakIdentityHashMap<R, T>()
operator fun getValue(thisRef: R, property: KProperty<*>): T =
map[thisRef] ?: setValue(thisRef, property, initializer(thisRef))
operator fun setValue(thisRef: R, property: KProperty<*>, value: T): T {
map[thisRef] = value
return value
}
}
Usage example:
var Int.tag: String by FieldProperty { "$it" }
fun main(args: Array<String>) {
val x = 0
println(x.tag) // 0
val z = 1
println(z.tag) // 1
x.tag = "my tag"
z.tag = x.tag
println(z.tag) // my tag
}
When defined inside a class, the mapping can be stored independently for instances of the class or in a shared delegate object:
private val bATag = FieldProperty<Int, String> { "$it" }
class B() {
var A.someCounter: Int by FieldProperty { 0 } // independent for each instance of B
var A.tag: String by bATag // shared between the instances, but usable only inside B
}
Also, please note that identity is not guaranteed for Java's primitive types due to boxing.
And I suspect the performance of this solution to be significantly worse than that of regular fields, most probably close to normal Map, but that needs further testing.
For nullable properties support and thread-safe implementation please refer to here.
You can't add a field, but you can add a property, that delegates to other properties/methods of the object to implement its accessor(s). For example suppose you want to add a secondsSinceEpoch property to the java.util.Date class, you can write
var Date.secondsSinceEpoch: Long
get() = this.time / 1000
set(value) {
this.time = value * 1000
}
If you are extending View you can do it quite easily like this...
This is example how I create some my custom class Event property in EditText class extension:
Define id for key :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="EditTextEventOnClearTagKey" type="id" />
</resources>
Define one reusable extension like this:
fun <T : Any> View.tagProperty(#IdRes key: Int, onCreate: () -> T): T {
#Suppress("UNCHECKED_CAST")
var value = getTag(key) as? T
if (value.isNull) {
value = onCreate()
setTag(key, value)
}
return value!!
}
Use it in wherever View extension you need:
val EditText.eventClear get() = tagProperty(R.id.EditTextEventOnClearTagKey) { event<Unit>() }