Kotlin - Understanding Getters and Setters - kotlin

Kotlin auto-generates it's getters and settings, but I never refer to them? Also, what is the correct way to write a custom getter/setter in Kotlin? When I say myObj.myVar = 99 I feel like myVar is a public field of myObj that I'm accessing directly? What is actually happening here?

This has been answered in a few places, but I thought that I would share a concrete example for people transitioning to Kotlin from Java/C#/C/C++, and who had the same question that I did:
I was having difficulty in understanding how getters and setters worked in Kotlin, especially as they were never explicitly called (as they are in Java). Because of this, I was feeling uncomfortable, as it looked like we were just directly referring to the vars/vals as fields. So I set out a little experiment to demonstrate that this is not the case, and that in fact it is the implicit (auto-generated) or explicit getter/setter that is called in Kotlin when you access a variable/value. The difference is, you don't explicitly ask for the default getter/setter.
From the documentation - the full syntax for declaring a property is:
var <propertyName>: <PropertyType> [= <property_initializer>]
[<getter>]
[<setter>]
And my example is
class modifiersEg {
/** this will not compile unless:
* - we assign a default here
* - init it in the (or all, if multiple) constructor
* - insert the lateinit keyword */
var someNum: Int?
var someStr0: String = "hello"
var someStr1: String = "hello"
get() = field // field is actually this.someStr1, and 'this' is your class/obj instance
set(value) { field = value }
// kotlin actually creates the same setters and getters for someStr0
// as we explicitly created for someStr1
var someStr2: String? = "inital val"
set(value) { field = "ignore you" }
var someStr3: String = "inital val"
get() = "you'll never know what this var actually contains"
init {
someNum = 0
println(someStr2) // should print "inital val"
someStr2 = "blah blah blah"
println(someStr2) // should print "ignore you"
println(someStr3) // should print "you'll never know what this var actually contains"
}
I hope that helps to bring it all together for some others?

Here are some real world examples of custom getters and setters. You can see more here.
// Custom getter example
val friendlyDescription get(): String {
val isNeighborhood = district != null
var description = if (isNeighborhood) "Neighborhood" else "City"
description += " in"
if (isNeighborhood) {
description += " $city,"
}
province?.let {
if (it.isNotEmpty()) {
description += " $it,"
}
}
description += " $country"
return description
}
print(myLocation.friendlyDescription) // "Neighborhood in Denver, Colorado, United States"
// Custom setter example
enum class SearchResultType {
HISTORY, SAVED, BASIC
}
private lateinit var resultTypeString: String
var resultType: SearchResultType
get() {
return enumValueOf(resultTypeString)
}
set(value) {
resultTypeString = value.toString()
}
result.resultType = SearchResultType.HISTORY
print(result.resultTypeString) // "HISTORY"

Related

kotlin getters and setters expecting member declaration error?

I'm starting to learn kotlin but I having problem implementing getters and setters I searched online and the code for my getters and setters is the same used by many guides.
package com.test
import kotlin.Int
class Test{
var name: Int = 10;
get(){
println("getting value");
return field;
}
set(value){
println("setting value");
field = value;
}
}
fun main() {
val test = Test();
println(test.name);
}
I cant see whats wrong in this code that make kotlin compiler emit an error. I'm compiling using kotlinc proj.kt.
You seem to really like adding semicolons, and you try to add them everywhere. However, you added them too early in this case:
var name: Int = 10;
get(){
println("getting value");
return field;
}
set(value){
println("setting value");
field = value;
}
This whole thing declares a property called name, so there should not be a semicolon after var name: Int = 10.
Your wrongly-added semicolon makes the parser think that the declaration for name ends there, and so the parser expects another declaration after that. Instead of another declaration, you wrote get() { ..., which only makes sense to the parser when you are declaring a property, but as far as the parser is concerned, you are not declaring a property at this point, as the declaration of name is already finished by your semicolon.
If you must add a semicolon, it would be after the } of set(value), like this:
var name: Int = 10
get(){
println("getting value");
return field;
}
set(value){
println("setting value");
field = value;
};
See also the grammar for a property declaration.
However, note that Kotlin coding convention says that you should omit semicolons whenever possible, so you should omit all your semicolons, like this:
var name: Int = 10
get(){
println("getting value")
return field
}
set(value){
println("setting value")
field = value
}

Kotlin DSL variable imitation

Using Kotlin type-safe builders one might end up writing this code
code {
dict["a"] = "foo"; // dict is a Map hidden inside that can associate some name to some value
println(dict["a"]); // usage of this value
}
This code is ok, but there is a problem: "a" is just a string. I want it to be like a user-defined variable - an identifier that is recognized by the compiler, auto-complete enabled.
Is there a way to turn it into something like this?
code {
a = "foo"; // now 'a' is not a Map key, but an identifier recognized by Kotlin as a variable name
println(a);
}
I can do this if I make code's lambda an extension function over some object with a field a defined inside. This is not what I want. I want to be able to use other variables (with unknown names) as well.
A possible workaround could be
code {
var a = v("a", "foo");
println(a);
}
Where v is a method of the extension's object, that stores value "foo" inside "dict" and also returns a handle to this value.
This case is almost perfect, but can it be clearer/better somehow?
You can use property delegation:
class Code {
private val dict = mutableMapOf<String, String>()
operator fun String.provideDelegate(
thisRef: Any?,
prop: KProperty<*>
): MutableMap<String, String> {
dict[prop.name] = this
return dict
}
}
Use case:
code {
var a by "foo" // dict = {a=foo}
println(a) // foo
a = "bar" // dict = {a=bar}
println(a) // bar
}

How to set non-null string variable's value to empty if null value passed

How can I achieve this java code's analog in Kotlin?
class Item {
private String text = "";
public String getText() {
return text;
}
public void setText(String str) {
if(str == null){
text = "";
} else {
text = str;
}
}
}
So whenever I set null value for text, it's value replaced with empty string.
I want exactly the same behavior but in Kotlin, because I'm working with Java classes in Kotlin code, which may return some null values. Checking for nullability everytime before setting fields value is not a good idea, because it can be forgotten by accident, and give an exception at runtime.
The simplest way is text.orEmpty()
text.orEmpty() for a String? will return "" if null, or the original string
The best alternate option is text ?: ""
class Item {
private var text: String = ""
fun getText() = text
fun setText(str: String?) {
text = str ?: ""
}
}
The Java code in the question implements a property whose value can't be null, but whose setter allows null (converting it to a default value).  (Java doesn't have direct support for properties, so you have to code the field, getter, and setter by hand.)
This doesn't really have an idiomatic Kotlin equivalent.  The direct translation would be:
class Item {
var text: String = ""
set(value) { field = value ?: "" }
}
This declares a non-nullable property with a default value.  (Like all Kotlin properties, its field is private; it also has a getter method and — because it's a var — a setter method, both of which are public because the no other visibility was specified.)  And it overrides the setter to substitute the default value if null is passed.
If called from Java code, then I think the above would handle it as expected.  (I haven't tested it.)
But because the Kotlin compiler knows that the field is not nullable, it wouldn't allow you to call the setter with a null value in the first place; this is where it differs from the Java version.
So if it were called only from other Kotlin code, the ?: "" would never be used, and so the setter wouldn't need to be overridden.  The most natural Kotlin translation would be simply:
class Item {
var text: String = ""
}
Some times the IDE will tell you that is unnecesary, so I made function and named ifNull then I passed the value.
private fun ifNull(value: String?): String {
return value ?: " "
}
Now, use it like this.
TextView.text = ifNull(name)

Infinite recursion in Getter in Kotlin

I am familiar with Java, but I am having difficulty working with Kotlin.
To illustrate my question, here is some Java Code. If the getter finds the field to be NULL, it initializes the field, before returning the field.
package test;
public class InitFieldJava {
private final static String SECRET = "secret";
private String mySecret;
public String getMySecret() {
if(mySecret == null) initMySecret();
return mySecret;
}
private void initMySecret() {
System.out.println("Initializing Secret ....");
mySecret = SECRET;
}
public static void main(String[] args) {
InitFieldJava field = new InitFieldJava();
System.out.println(field.getMySecret());
}
}
Can I do something like the above in Kotlin. My attempt in Kotlin looks like this:
package test
class InitFieldKotlin {
private val SECRET = "secret"
private var mySecret: String? = null
get() {
if (mySecret == null) initMySecret() //Infinite Recursion!!!
return mySecret
}
private fun initMySecret() {
println("Initializing Secret ....")
mySecret = SECRET
}
companion object {
#JvmStatic
fun main(args: Array<String>) {
val field = InitFieldKotlin()
println(field.mySecret)
}
}
}
My problem is that this results in infinite recursion:
Exception in thread "main" java.lang.StackOverflowError
at test.InitFieldKotlin.getMySecret(InitFieldKotlin.kt:7)
at test.InitFieldKotlin.getMySecret(InitFieldKotlin.kt:7)
at test.InitFieldKotlin.getMySecret(InitFieldKotlin.kt:7)
at test.InitFieldKotlin.getMySecret(InitFieldKotlin.kt:7)
I’d appreciate knowing what I’m doing wrong.
Try to use field keyword inside get():
private var mySecret: String? = null
get() {
if (field == null) initMySecret()
return field
}
Generally speaking, field allows to access your value directly without calling get, almost in the same way as in your Java example. More information can be found in documentation.
The problem you're facing is that when you call your property this way, the getter will be called again. And when you call getter, another getter is called, and so on until an StackOverflow.
You can fix this as shown by #Google, and using field inside the getter, instead of the property name:
if (field == null)initMySecret()
This way you won't access the property using its getter.
But more importantly: why don't you use a lazy initialization? If the variable is final, and it seems to be, you could use a lazy val
This way, the field won't be nullable anymore, so you won't have to safe-call it. And you'll not use boilerplate code, Kotlin can do this lazy initialization for you!
val mySecret: String by lazy {
println("Initializing Secret. This print will be executed only once!")
"SECRETE" //This value will be returned on further calls
}
More examples on Lazy can be seen at Kotlin Docs

How can I set the JsName for a property's backing field in Kotlin?

I played about with Kotlin's unsupported JavaScript backend in 1.0.x and am now trying to migrate my toy project to 1.1.x. It's the barest bones of a single-page web app interfacing with PouchDB. To add data to PouchDB you need JavaScript objects with specific properties _id and _rev. They also need to not have any other properties beginning with _ because they're reserved by PouchDB.
Now, if I create a class like this, I can send instances to PouchDB.
class PouchDoc(
var _id: String
) {
var _rev: String? = null
}
However, if I do anything to make the properties virtual -- have them override an interface, or make the class open and create a subclass which overrides them -- the _id field name becomes mangled to something like _id_mmz446$_0 and so PouchDB rejects the object. If I apply #JsName("_id") to the property, that only affects the generated getter and setter -- it still leaves the backing field with a mangled name.
Also, for any virtual properties whose names don't begin with _, PouchDB will accept the object but it only stores the backing fields with their mangled names, not the nicely-named properties.
For now I can work around things by making them not virtual, I think. But I was thinking of sharing interfaces between PouchDoc and non-PouchDoc classes in Kotlin, and it seems I can't do that.
Any idea how I could make this work, or does it need a Kotlin language change?
I think your problem should be covered by https://youtrack.jetbrains.com/issue/KT-8127
Also, I've created some other related issues:
https://youtrack.jetbrains.com/issue/KT-17682
https://youtrack.jetbrains.com/issue/KT-17683
And right now You can use one of next solutions, IMO third is most lightweight.
interface PouchDoc1 {
var id: String
var _id: String
get() = id
set(v) { id = v}
var rev: String?
var _rev: String?
get() = rev
set(v) { rev = v}
}
class Impl1 : PouchDoc1 {
override var id = "id0"
override var rev: String? = "rev0"
}
interface PouchDoc2 {
var id: String
get() = this.asDynamic()["_id"]
set(v) { this.asDynamic()["_id"] = v}
var rev: String?
get() = this.asDynamic()["_rev"]
set(v) { this.asDynamic()["_rev"] = v}
}
class Impl2 : PouchDoc2 {
init {
id = "id1"
rev = "rev1"
}
}
external interface PouchDoc3 { // marker interface
}
var PouchDoc3.id: String
get() = this.asDynamic()["_id"]
set(v) { this.asDynamic()["_id"] = v}
var PouchDoc3.rev: String?
get() = this.asDynamic()["_rev"]
set(v) { this.asDynamic()["_rev"] = v}
class Impl3 : PouchDoc3 {
init {
id = "id1"
rev = "rev1"
}
}
fun keys(a: Any) = js("Object").getOwnPropertyNames(a)
fun printKeys(a: Any) {
println(a::class.simpleName)
println(" instance keys: " + keys(a).toString())
println("__proto__ keys: " + keys(a.asDynamic().__proto__).toString())
println()
}
fun main(args: Array<String>) {
printKeys(Impl1())
printKeys(Impl2())
printKeys(Impl3())
}
I got a good answer from one of the JetBrains guys, Alexey Andreev, over on the JetBrains forum at https://discuss.kotlinlang.org/t/controlling-the-jsname-of-fields-for-pouchdb-interop/2531/. Before I describe that, I'll mention a further failed attempt at refining #bashor's answer.
Property delegates
I thought that #bashor's answer was crying out to use property delegates but I couldn't get that to work without infinite recursion.
class JSMapDelegate<T>(
val jsobject: dynamic
) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return jsobject[property.name]
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
jsobject[property.name] = value
}
}
external interface PouchDoc4 {
var _id: String
var _rev: String
}
class Impl4() : PouchDoc4 {
override var _id: String by JSMapDelegate<String>(this)
override var _rev: String by JSMapDelegate<String>(this)
constructor(_id: String) : this() {
this._id = _id
}
}
The call within the delegate to jsobject[property.name] = value calls the set function for the property, which calls the delegate again ...
(Also, it turns out you can't put a delegate on a property in an interface, even though you can define a getter/setter pair which work just like a delegate, as #bashor's PouchDoc2 example shows.)
Using an external class
Alexey's answer on the Kotlin forums basically says, "You're mixing the business (with behaviour) and persistence (data only) layers: the right answer would be to explicitly serialise to/from JS but we don't provide that yet; as a workaround, use an external class." The point, I think, is that external classes don't turn into JavaScript which defines property getters/setters, because Kotlin doesn't let you define behaviour for external classes. Given that steer, I got the following to work, which does what I want.
external interface PouchDoc5 {
var _id: String
var _rev: String
}
external class Impl5 : PouchDoc5 {
override var _id: String
override var _rev: String
}
fun <T> create(): T = js("{ return {}; }")
fun Impl5(_id: String): Impl5 {
return create<Impl5>().apply {
this._id = _id
}
}
The output of keys for this is
null
instance keys: _id
__proto__ keys: toSource,toString,toLocaleString,valueOf,watch,unwatch,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,__defineGetter__,__defineSetter__,__lookupGetter__,__lookupSetter__,__proto__,constructor
Creating external classes
Three notes about creating instances of external classes. First, Alexey said to write
fun <T> create(): T = js("{}")
but for me (with Kotlin 1.1) that turns into
function jsobject() {
}
whose return value is undefined. I think this might be a bug, because the official doc recommends the shorter form, too.
Second, you can't do this
fun Impl5(_id: String): Impl5 {
return (js("{}") as Impl5).apply {
this._id = _id
}
}
because that explicitly inserts a type-check for Impl5, which throws ReferenceError: Impl5 is not defined (in Firefox, at least). The generic function approach skips the type-check. I'm guessing that's not a bug, since Alexey recommended it, but it seems odd, so I'll ask him.
Lastly, you can mark create as inline, though you'll need to suppress a warning :-)