Is it possible to get a reference to a variable in Kotlin? - kotlin

Is it possible to get a reference to a variable in Kotlin?
Example:
var vIndex = 0
::vIndex
Not supported [Variable references are not yet supported]
Can this be bypassed somehow?

Rather than writing:
var vIndex = 0
val propertyReference = ::vIndex // error
You can put the local property into an anonymous object:
val foo = object {
var vIndex = 0
// you can also put other local properties that you want to reference here
// in the same object, provided that they are in the same scope
}
val propertyReference = foo::vIndex
propertyReference here will be of type KMutableProperty0<Int>, just as you would expect. You can access/mutate foo.vIndex the same way you can the local property vIndex.
Of course, this has the additional overhead of creating an additional class file for the anonymous object, but alas, a workaround is a workaround.

As stated in your question, (local) variable references are not yet supported. But you can have a reference to a property, i.e. you can write:
class VariableReferences {
var vIndex = 0
var ref = ::vIndex
}

Related

What is the difference between declaring a view variable to using it directly on Kotlin?

In Kotlin, as I know, you can call a view in two ways:
Declaring a variable
val btn: Button = btnLogin
btn.text = "newLogin"
Using it directly
btnLogin.text= "newLogin"
So what are the differences between these implementations?
I know that declaring a variable may be more useful when calling repeatedly and may prevent typos, but what are the advantages?
Assuming btnLogin is a nullable property, then declaring a local variable allows you to acquire a non-null reference after checking for null. For example:
private var btnLogin: Button? = null
override fun onViewCreated(view: View) {
btnLogin = view.findViewById(R.id.my_button)
}
private fun otherMethod() {
val loginButton = btnLogin
if (loginButton != null) {
loginButton.doX()
loginButton.doY()
loginButton.doZ()
}
}
Since the property can be changed from non-null to null at any time, capturing the current value to a local variable allows you to capture a fixed value at the point where it's declared. Kotlin does have many other constructs you can use instead of capturing a local variable, however. For example, this would be equivalent:
btnLogin?.let { loginButton ->
loginButton.doX()
loginButton.doY()
loginButton.doZ()
}
In general, capturing a local variable just ensures that the same value is referenced throughout, but in most cases, it's likely not necessary.

Kotlin: Should mutableList be declared as Val or Var?

I am reading through the existing codebase for my team, and I notice mutableListOf are always declared as val. In some scenarios, elements are added to the mutableListOf only once. E.g
val jobList = mutableListOf<JobActivity>()
jobList.addAll(job.activities)
In other scenarios, elements are added to mutableListOf in a loop. E.g
val jobList = mutableListOf<JobActivity>()
newJobList.filterScanType(retrieveJobType(JobContext.NEW)).forEach {
jobList.add(it)
}
Since the list is not initialized on creation, why not declare mutableListOf as var? A lot of examples found online also follow the same pattern of declaring mutableListOf as val.
Which is best to use in the 2 scenarios described, val or var?
I think it's declared as val because the list will be the same always, the only thing that changes is it's elements. You'll never do something like:
joblist = anotherList
And as #Taseer said, the properties of the object can be changed even if it's a val. For example:
data class Example(var name: String)
val exampleObject = Example("SomeName")
You'll still be able to do this:
exampleObject.name = "AnotherName"
But you can't do this:
exampleObject = anotherObject
The general rule of thumb while using Kotlin.
Difference in val and var
You may already know the differences but for the sake of an answer, I will repeat it. var lets you modify the reference of an object while val does not permit to change the reference of an object.
An object can be declared safely using either var or val keyword but the reason why you want to use val on an object(in most cases) is that you don't want to refer that class member with a new reference of a new instance of an object. That way, you always keep a reference to the original object and you can modify object properties.
In the case of var, though nothing wrong with it, you can still use it 'without' any problems. You can still access the object properties and modify them and also you will be able to refer that class member to a reference of a new object.
Example:
val myObject = MyObject()
myObject.something = 1 //can still modify object property.
myOjbect = MyObject() //re-referencing the object, NOT POSSIBLE
var myNewObject = MyNewObject()
myNewObject.someThing = "Hello world!" //can still modify object properties
myNewObject = MyNewObject() //can still reference it.
Why to use val over var in case of 'immutable' objects?
It gives you the security of not 'accidentally' placing a new reference.
But is there any performance benefit using val?
Answer: Final keyword benefit
val is more idiomatic for the reasons given in other answers and comments.
You said the val is not instantiated, but in your example code, it is.
val jobList = mutableListOf<JobActivity>()
is a factory that instantiates an empty MutableList<JobActivity>
Using this pattern (val not var, instantiated upon declaration) ensures that your code will never find an uninitialized or null value for jobList; and the compiler can prove it.
In short - there are no rules, its up to you
if you use val you can modify mutableList, but not reassign
if you need reassign another list to same variable, use var. In most cases you dont need it, thats why your team uses it frequently
Whether a variable is var or val distinguishes between a variable of which the value (reference) can be changed (var = mutable) or not (val = immutable).
You should always strive to use val over var to avoid unwanted side-effects (changing it in another thread for example).
In case of the MutableList you should most likely use a val, because you don't want to mutate the reference to the list but rather its contents.
Here an overview of your options:
// Do you want to change its reference (r) / contents (c)?
var a = mutableListOf(1, 2, 3) // r = yes, c = yes
var b = listOf(1, 2, 3) // r = yes, c = no
val c = mutableListOf(1, 2, 3) // r = no, c = yes
val d = listOf(1, 2, 3) // r = no, c = no
You create a variable with var that is mutable (that can change). Mutable means variable can be changed in future.
val is used when variable will not be changed in future that means constant or final.
Here changed means new value or new things will be assigned to the variable but
val list = mutableListOf()
in this list variable you assigned mutable list. You just changed the value of the list. But you didn't assign new instance or new value to the variable you just added and remove value from the list. That's it. So here list is immutable itself.
It will be mutable if you do things like below...
var list = mutableListOf()
list = mutableListOf()
Two initialization on the same variable.

Kotlin and constructors, initializing

Sorry for asking a very newbie Kotlin question, but I'm struggling to understand some of the things related to constructors and intitializing.
I have this class and constructor:
class TestCaseBuilder constructor(
caseTag: String = "Case",
applType: Buy.ApplFor = Buy.ApplFor.PROOFFINANCE,
komnr: String = "5035") {
var caseTag: String = caseTag
var applType: Buy.ApplFor = applType
var komnr: String = komnr
What I'm trying to do here is to have three optional parameters in the constructors, using default values for them. The reason I'm declaring them in the class body is because I need to have access to them from the main class.
Now, this code works. No errors when I run it. But IntelliJ gives the following comment for the variables (ex.: caseTag):
Property is explicitly assigned to parameter caseTag, can be declared
directly in constructor.
What I've found when searching this is examples using an init {}, but the result I've gotten to includes initializing the variables twice, once in the constructor and then in the init {}. Which clearly isn't correct, I'd say?
What's a better what to have (or than having) optional parameters in the constructor, and then creating class variables from them?
You can declare properties directly in primary constructor. That means you can drop explicit declarations in class body:
class TestCaseBuilder constructor(
var caseTag: String = "Case",
var applType: Buy.ApplFor = Buy.ApplFor.PROOFFINANCE,
var komnr: String = "5035")
You can also drop the constructor keyword if your primary constructor does not have any annotations or visibility modifiers (defaults to public).
#JvmOverloads annotation can over load the constructor with different param size
class TestCaseBuilder #JvmOverloads constructor(
var caseTag: String = "Case",
var applType: Buy.ApplFor = Buy.ApplFor.PROOFFINANCE,
var komnr: String = "5035"
)
Then the class got three constructor with optional param
val a = TestCaseBuilder("CaseA")
val b = TestCaseBuilder("CaseB", Buy.ApplFor.SomethingElse)
val c = TestCaseBuilder("CaseB", Buy.ApplFor.SomethingElse, "1111")

ArrayList<AbstractObject> adding objects which extended AbstractObject is not possible. How to fix it?

So I have ArrayList<AbstractObject> which is class type abstract. And I have 2 items which extends AbstractObject. If I use abstractList.add(Object1) it says that ArrayList expects object of type AbstractObject and not Object1. I thought that this is possible. Reason why I want to do this is to use multiple objects with 2 different data in single RecyclerView. (ViewTypes)
abstract class ListItem {
abstract val type: Int
companion object {
const val TYPE_HEADER = 0
const val TYPE_ITEM = 1
}
}
class HeaderItem(val headerTitle: String) : ListItem() {
val type: Int
get() = TYPE_HEADER
}
class ObjectItem(val object: ParseObject) : ListItem() {
val type: Int
get() = TYPE_ITEM
}
Init #1:
var recyclerViewArray: ArrayList<out ListItem> = ArrayList()
This is error if I want to add HeaderItem to this list:
Init #2:
var recyclerViewArray: ArrayList<ListItem> = ArrayList()
This says Type mismatch. Tried with as but as is yellowed with message This cast can never succeed.
It is possible and it works fine. If in some expression Kotlin infers the type wrong, you can always specify it manually. In your case
abstractList.add(Object1 as AbstractObject)
Remove out from the declaration of recyclerViewArray and it should work (I just tried it and it ran fine). e.g. I could run this line of code:
recyclerViewArray.add(HeaderItem("test"))
Note that when you remove out you still need to keep the fact that it's an ArrayList of ListItem objects. So you should declare it as:
var recyclerViewArray = arrayListOf<ListItem>()
Some of your code didn't quite compile for me, like having a property called object (I had to put backticks around that) and not putting the override modifier on the type property on HeaderItem.

In Kotlin, is it possible to use a variable to call a method or property?

Simply put, I have a variable that tells me which property I need to modify on an object, but cannot call that property AS the variable.
data class MyDataClass(var one: String, var two: Int)
fun doSomething() {
myData = MyDataClass("first", 2)
val propertyImInterestedIn = "one"
myData.{propertyImInterestedIn} = "second" // How do I do this?
assertEquals("second", myData.one)
}
You can either do it at compile time if You can directly reference the fields, or at runtime but you will lose compile-time safety:
// by referencing KProperty directly (compile-time safety, does not require kotlin-reflect.jar)
val myData = MyDataClass("first", 2)
val prop = myData::one
prop.set("second")
// by reflection (executed at runtime - not safe, requires kotlin-reflect.jar)
val myData2 = MyDataClass("first", 2)
val reflectProp = myData::class.memberProperties.find { it.name == "one" }
if(reflectProp is KMutableProperty<*>) {
reflectProp.setter.call(myData2, "second")
}
You can use the Kotlin reflection API to do that, and bound callable references in particular:
val propertyImInterestedIn = myData::one
propertyImInterestedIn.set("second")
Note that you need to add kotlin-reflect as a dependency to your project.