How can I initialize a companion object property with a value taken from application.properties? - kotlin

Is it possible to initialize a property of a companion object with some value read from application.properties?
I tried
// #Value("\${my.property.value}") ...doesn't work
val myProp: Duration by lazy { Duration.ofMinutes(#Value("\${my.property.value}")) }
but IntelliJ complains: Expecting an element.
Okay Duration.ofMinutes() requires a Long not a String but how can I achieve this using a configurable value in application.properties?

I solved it by using the #Value annotation in a service class now.
My intention was to have this value read in into a single place near the entity.

Related

Understanding the impact of var and val with respect to getters and setters of a property

Let's say I have a Kotlin class Dog with two properties weight and weightInKgs
class Dog(val weight: Double) {
// property without initializing works. Why?
val weightinKgs: Double
get() = weight/ 2.2;
}
The above code runs without errors. I know that every property in Kotlin must be initialized so why does defining a getter without initializing the property work? Secondly, when val is changed to var for weightInKgs, it produces an error asking for initialization. How does changing it to var break the code?
class Dog(val weight: Double) {
// well its doesn't work now.
var weightinKgs: Double
get() = weight/ 2.2;
}
Every property with a backing field must be initialized. A property has a backing field if any of the following is true:
You initialize the backing field at the declaration site using =.
It has a custom getter or setter that references field.
It uses the implicit getter or setter, which implicitly uses field.
Otherwise, it does not have a backing field.
If there is no backing field used by the getter and/or setter, there is no need to initialize one. Your first code block has a custom getter that doesn't use field.
In your second code block, you have a var and it's using the implicit setter, which uses the backing field, so the backing field must be initialized.
If it's not obvious, get() is a function that calculates a value (weight / 2.2) every time you call it. It's basically the equivalent to this
fun getWeightInKgs(): Double {
return weight / 2.2
}
So that's why it doesn't have a backing field, it's not actually storing a value. But Kotlin presents these kinds of getX() functions (and set, is etc) as properties, and encourages you to use property access syntax, so dog.weightInKgs instead of dog.getWeightInKgs(). Kinda hiding the specific implementation details
If you didn't want to calculate the weight every time, and just wanted to do it once, then you'd just do
val weightInKgs = weight / 2.2
and then it would have a backing field, because that value has to be stored somewhere. You could also have a getter function that refers to a private val or var and returns the value of that, instead of giving the property itself a backing field, but if you ever need to do that kind of thing you'll probably understand why you would! That's usually for when your getter and/or setter is doing something a bit more complicated than just hiding or validating an internal data value

Collection<KProperty1<I,*>> How to get the property instance

I'm currently using Reflection to inspect an element at runtime using the class.memberProperties function. The type of properties is collection<KProperty1<I, *>> so I run through each of the KProperty objects to find the one that I want by checking if the name is equal to "nameIWant", though I would much rather be able to get the instance of the property from the KProperty by using the .get() method on the property, so that then I could do a check such as:
if (property.get(receiver) is ClassIWant) {
//Do something
}
My code looks like this:
val properties = request.payload::class.memberProperties
properties.forEach { property ->
run {
if (property.name.equals("nameIWant")) {
}
}
}
So far I've been trying to use the .get() method on the KProperty1 type but it takes an argument receiver of type Nothing. I'm not able to work out what I need to pass in order to call the .get() method and get the particular instance of the property. I've also checked the documentation here: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-property1/index.html but it hasn't really helped at all.
justPassingBy is right. but the more simple way is to use:
myObj.javaClass.kotlin.memberProperties.foreach { property ->
property.get(myObj)
}
If you want to get the value of the property, cast the class into invariant type.
instance::class.memberProperties.first() // returns KProperty1<out Instance, *>
(instance::class as KClass<Instance>).memberProperties.first() // returns KProperty1<Instance, *>
If your KClass<Instance> is KClass<*>, use Any as Instance.
Why did the KProperty.call take Nothing as receiver?
Because instance::class returns KClass<out Instance>, which propagates the covariant type argument down to the property, which it becomes KProperty<out Instance, *>, which narrows down the possible method receiver to any subtype of Instance, but because we do not know which, we can not safely supply any instance of Instance, as show by the rules of variance, which here limit the generic type argument to Nothing, which means it is impossible to call the method at all.
Why is ::class designed to be covariant?
To guarantee safety. This has been an issue of great debates as it seems somewhat illogical.
If you want to know the type of the value that the property can return, use
property.returnType
It returns a KType, wich is Kotlin's version of Java's Type, which is a more generic concept of a Class (which is one of the implementations of Type).
If you need to 'convert' the KType to a KClass, you need to do the same as if you needed to convert Type to a Class, which is get the raw type of the type. Raw type is type stripped of the any generic information, yes, an erased type. The way to do this is (seemingly) more complicated (involves handling each possible KType/Type implementation) and I recommend checking for answer to this problem separately.
You will be able to reuse Java implementation (that you will surely find on your own) using:
kType.javaType.covertJavaTypeToJavaClass().kotlin // returns KClass<*>
Corrections in your question. I recommend using the proper terms if you wish to receive proper answers:
* I in your question is type of the method receiver, not the value of the property
* collection is not a type, Collection is
* property is ClassIWantis ambiguous as property.type is type of the value in the property and property::class is simply the property implementation, is is also an instanceof check, but in reflection, you need to use KClass.isSubclassOf, or what is known in Java as type.isAssignableFrom (watch the call order), which then makes your condition to be ClassIWant.isSuperclassOf(property.type.getRawType())
* instance of the property properties have values, not instances. Only classes have instances. Instances are values and values are instances (of some class), but you must still say instance representing the value of the property
You can create a KType for your ClassIWant and then check the property's returnType. It will be something like this:
val properties = request.payload::class.memberProperties
val desiredType = ClassIWant::class.createType()
properties.forEach { property ->
if (property.name == "nameIWant" && property.returnType == desiredType) {
//todo
}
}
btw you can cast your property variable to correct type and use get
val properties = request.payload::class.memberProperties
properties.forEach { property ->
val value = (property as KProperty1<Payload, *>).get(request.payload)
if (property.name == "nameIWant" && value is ClassIWant) {
//todo
}
}
prop.getter.call(obj) as String?

How to get the name of a variable in Kotlin?

I have a Kotlin class in my application with a lot of attributes, what I want to build is a method that stores the variable name in a dictionary. The dictionary looks like this:
HashMap<String, Pair<Any, Any>>()
The purpose of this is to store the changes made to a certain attribute, I store the name of the variable as the key and in the Pair I store the old value and the new value. To notify a change I use the Observer pattern. So whenever a setter is called from an attribute a change will be notified and stored to the dictionary.
The code below results in the folowing:
var person = Person("Harry", 44)
person.age = 45
HashMap("age", (44, 45))
Right now I am just hardcoding the variable name in as a String, so my question is:
How to dynamicly get the name of a variable in Kotlin?
I saw the same question in Java: Java Reflection: How to get the name of a variable?
Also some other questions about the same topic claiming it is not possible: Get the name property of a variable
I can understand that it is not possible to get the name of a variable, because the compiler simple doesn't have that information, but I am still currious to see if others have any sollution for this problem.
As stated in the Kotlin documentation about Reflection:
val x = 1
fun main() {
println(::x.get())
println(::x.name)
}
The expression ::x evaluates to a property object of type KProperty<Int>, which allows us to read its value using get() or retrieve the property name using the name property.
Use memberProperties to get the names of the class attributes and others properties. For instance:
YourClass::class.memberProperties.map {
println(it.name)
println(it.returnType)
}
I think delegate properties is the solution to my problem:
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
Credits go to: Roland
Source: https://kotlinlang.org/docs/reference/delegated-properties.html

In Kotlin what does this get() do

Im new to Kotlin and wonder what does the get() = login_email.txt.toString() do?
Does it set email String?
get() and set(value) after a field means the declaration of a custom getter and/or setter. Here's a basic example using default values:
class Demo{
var something: String
get() = field
set(value) {
field = value;
}
constructor(something: String){
this.something = something;
}
}
These two are, however, redundant. You don't actually need them unless you're doing something custom with it. They're automatically added for vars, though that only applies to getters for vals (because they can't be changed, they don't have setters).
The line you were asking about is a custom getter.
get() // declares a custom getter
= // if you don't know how this works, see my explanation below
login_email.text.toString() // you should be familiar with this part already; gets the string value of the field
If you're not familiar with the syntax, this is the equivalent without =:
get(){
return login_email.text.toString()
}
if you have a single return, you can replace the brackets and return keyword with =. If it helps you remember, just remember the alternative to using = (a body + the return keyword)
TL;DR: it declares a custom setter that returns the value of a TextView/EditText (not sure which it is, you didn't include that in the question)
In your case, you're using a custom getter or setter to handle property data. The fields themselves don't actually contain any data, but you have getters for a different object.
Take this as an example:
class Demo(private val someObject: MyCustomObjectWithSomeData){
val text: String
get() = someObject.text
... same for other stuff. Could also have setters, if the properties are mutable
}
Here the object is private, but it could be public or internal for that matter.
Kotlin supports quite a lot with custom getters. For an instance, you can declare a field to display specific fields of a private variable. For an instance, in your case, you have the email. It doesn't need to be a variable, since you have a custom getter, and the field isn't initialized. If you change var email to a val, you can make it non-null:
val email: String
get() = login_email.text.toString()
That also helps with null-safety.
And for the error field, it's slightly more complicated. It can't be a val because you declare a custom setter, but if you add a getter, you can make it non-null:
var error: String
get() = login_error.text.toString()
set(value){
login_error.text = value;
}
Short Answer
get() is used to define a custom getter method. Anytime you access the property you are using the custom getter method
Long Answer
So before we can talk about get(), it is important that we get a proper understanding of what properties actually are.
Properties
We all know that in object oriented programming the main idea of a class is to encapsulate data and code that works on data in a single class. In a language like Java (don't worry we will get back to Kotlin soon), the data of a class is stored in private fields, we then use accessor method (getters and setters) to access the data. In Java the combination of accessor methods and a field is called a property
Now in Kotlin does things a little differently, it entirely replaces the traditional idea of defining accessor methods and fields. By using the val or var keyword Kotlin will automatically generate the corresponding field and appropriate accessor methods for us.
get()
There will come a time when either your code or someone else's code needs a more robust solution to the automatic accessor methods created by Kotlin. This is where get() and set() come into play. By using get() you are defining your own custom accessor method(getter for get()) to be used when you are accessing this property.
I would also like to point out that since val is immutable it does not allow you to define a set() method, only a get()

Why Property must be initialized when there is auto back-end field generated

I'm new to properties and moved from the java to kotlin. I'm struggling with the properties, I learned much about it but initializing the properties are confusing me, when it should be initialized or when it can work without initialization.
Let me explain it by the help of code. Below is the code which is requiring to initialize the property when the back-end field generated, before posting the code let me post the paragraph from the kotlin official website.
A backing field will be generated for a property if it uses the
default implementation of at least one of the accessors, or if a
custom accessor references it through the field identifier.
Now here is the code below.
class Employee{
var data: String // because there are default implementation of get set
// so there will be a back-end field.
}
So I have to initialize it else compilation error.
Ok I can understand it as that some one can access it so there will be no value which can produce the wrong result.
Then I move next to understand it more, so I add custom getter.
class Employee{
var data: String
get() = "default value"
}
This also generate the back-end field so compilation error to initialize it. I can understand it as that there is no initialized value so compiler complain about it.
May be compiler is not smart enough yet to check that there is value which is giving result for this property by custom getter so don't complain about initializing just return that value when required.
But there should be not a problem if any one access it then a default value is already there, then why compiler still complain?
Then I move one step more to implement custom setter too.
class Employee{
var data: String
get() = "default value"
set(value){
field = value
}
}
Still there is the back-end field because we have accessed the field so compiler generate the back-end field.
Same error, should be initialized.
Then the final stage where it works fine as below.
class Employee{
var data: String
get() = "default value"
set(value){
}
}
Now I'm not accessing field in custom getter setter so there is not a back-end field. And it works fine.
So the final question when the property should be intialized? When there is a back-end field generated?
Yes this does not compile:
class Employee{
var data: String
get() = "default value"
}
but this does:
class Employee{
val data: String
get() = "default value"
}
so maybe the compiler by stating Property must be initialized for the wrong declaration, wants from you to admit that data is something that you can not change. I say maybe.
Now the part that does compile:
class Employee{
var data: String
get() = "default value"
set(value){
}
}
This is where you explicitly admit that whatever happens I will never set a value to data, and that's why the compiler feels fine.
Just to save you from more confusion, there's a lot of explaining about Kotlin in the Internet and you may find it very difficult to get familiarized with this relatively new language, but keep in mind that everything needs to be tested by you.
I found the below code in a web page:
class User{
var firstName : String
get() = field
set(value) {field = value}
var lastName : String
get() = field
set(value) {field = value}
}
and it is presented as compilable when it's not.
You kind of answered your own question. There's no backing field when you override both getter and setter, and don't access field.
About your "compiler not being smart enough": get() function is actually RAN at runtime, so writing a lot of compiler code just to evaluate if return value is static and should be injected as default is too niche of a use case.
If your getter depends on another field which is only initialized later, this would cause a lot of confusion as to what default value should be.
Consider this code, assuming value of provider is not defined:
var data: String
get() = provider.data
What would be a default value? Do you want a null? Empty string? Maybe entire object initialization should crash? Explicit default value declaration is needed for that purpose.
That's where idea of lateinit var came to be: if You're certain you will set value before performing any get, You can use this keyword to prevent compiler errors and setting default value.
class Employee{
var data: String
get() = "default value"
}
var means there are both a getter and a setter. Because you didn't write a setter, you get the default one, which accesses the backing field. So there is a backing field, and it needs to be initialized.
But there should be not a problem if any one access it then a default value is already there, then why compiler still complain?
Because that makes the rules simpler: all properties with backing fields must be initialized. This in turn may be because in Java fields don't have to be initialized and this is a known source of bugs. I would like to say it also avoids a possible bug, because presumably you don't actually want the setter's result never to be accessible, but initializing doesn't fix that problem.
I don't see any obvious problem with changing the rules so that a field only needs to be initialized when accessed in the getter, and maybe adding a warning when only one accessor uses field. But I may be missing something, and don't see much benefit to doing so either.