How to skip defined getters or setters in Kotlin - kotlin

In java you can do the follwing:
public class Foo {
private String bar = "text";
public void method() {
// direct access (no logic)
System.out.println(this.bar);
}
// only if you access the object from the outside
// you are forced to use the getter with some logic in it
public String getBar() {
System.out.println(this.bar);
return this.bar;
}
}
But if you define a getter or a setter with logic in Kotlin you are forced to always execute this logic when accessing the field:
class Foo {
var bar: String = "text"
get() {
println(field)
return field
}
private set
fun method() {
// this also executes the getter
// Is it possible to skip the getter
// and directly access the field?
println(this.bar)
}
}
Is there a better way to access the field without executing the getter or setter logic than creating your own fun getBar() in Kotlin?

There is no possible way to skip a getter or a setter, they are intended to block the direct access of a property.
What you can do is make a multi-reference to same value (fake-referencing):
private var _bar: String = "text"
var bar
get() {
// some operations intercepting the getter
return _bar
}
// direct access
_bar
// intercepted access public field
bar

In Kotlin the backing fields (in your case the private variable) are not exposed by design. There are a few exceptions explained here: https://kotlinlang.org/docs/reference/properties.html#backing-fields
All access to val and var happens through implicit getters and setters. A val resolves to a property with a getter() while var resolves to a property with a getter and a setter: https://kotlinlang.org/docs/reference/properties.html#properties-and-fields

Related

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

When creating an interface in Kotlin, does it matter if properties have get/set?

In a Kotlin interface, does it matter if properties are declared with empty get/set statements?
For instance...
interface ExampleInterface {
// These...
val a: String
get
var b: String
get
set
// ...compared to these...
val c: String
var d: String
}
I'm having a hard time noticing a difference.
When implementing the interface, it doesn't seem to matter if I use getters/setters for the properties, or if I set the value directly.
When accessing these through java, the val's both have getters, and the var's both have getters and setters.
public void javaMethod(ExampleInterface e) {
e.getA();
e.getB();
e.setB();
e.getC();
e.getD();
e.setD();
}
The property declarations in your example are identical, get and set can be safely removed from there, because, as you correctly noted, the accessors are generated anyway. The syntax with get and set can, however, be used to provide an accessor implementation or to restrict its visibility.
Providing implementation:
interface ExampleInterface {
var b: String
get() = ""
set(value) { }
}
This example shows a default implementation of a property declared in an interface. This property can still be overriden inside the interface implementations.
class Example {
var b: String = ""
get() = "$field$field"
}
Here, get() = ... overrides the default getter behavior of a property with a backing field, whereas set is not mentioned, thus it behaves normally.
Visibility restriction:
class Example {
var s: String = "s"
private set
}
In this example, the setter visibility is private. The visibility of get is always the same to the visibility of the property, so there's no need to specify it separately. Interfaces cannot declare private members.
abstract class Example {
abstract var b: String
protected set // Restrict visibility
}
The setter of this property is restricted to this class and its subclasses. Interfaces cannot declare protected members.
Of course, an accessor implementation can be combined with visibility restriction:
class Example {
var s: String = "abc"
private set(value) { if (value.isNotEmpty()) field = value }
}
See also:
The Kotlin reference article about properties
Properties visibility explanation in another answer

Passing parameters to a custom getter in kotlin

I have been reading about properties in Kotlin, including custom getters and setters.
However, I was wondering if it is possible to create a custom getter with extra parameters.
For example, consider the following method in Java:
public String getDisplayedValue(Context context) {
if (PrefUtils.useImperialUnits(context)) {
// return stuff
} else {
// return other stuff
}
}
Note that the static method in PrefUtils has to have Context as a parameter, so removing this is not an option.
I would like to write it like this in Kotlin:
val displayedValue: String
get(context: Context) {
return if (PrefUtils.useImperialUnits(context)) {
// stuff
} else {
// other stuff
}
}
But my IDE highlights all of this in red.
I am aware I can create a function in my class to get the displayed value, but this would mean I would have to use .getDisplayedValue(Context) in Kotlin as well instead of being able to refer to the property by name as in .displayedValue.
Is there a way to create a custom getter like this?
EDIT: If not, would it be best to write a function for this, or to pass Context into the parameters of the class constructor?
As far as I know, property getter cannot have parameter. Write a function instead.
You can do this by having a property that returns an intermediate object that has a get and/or set operator with the parameters that you want, rather than returning the value directly.
Having that intermediate object be an inner class instance may be useful for providing easy access to the parent object. However, in an interface you can't use inner classes so in that case you might need to provide an explicit constructor parameter referencing the parent object when constructing your intermediate object.
For instance:
class MyClass {
inner class Foo {
operator fun get(context: Context): String {
return if (PrefUtils.useImperialUnits(context)) {
// return stuff
} else {
// return other stuff
}
}
}
val displayedValue = Foo()
}
...
val context : Context = whatever
val mc : MyClass = whatever
val y: String = mc.displayedValue[context]
You can do for example:
val displayedValue: String by lazy {
val newString = context.getString(R.string.someString)
newString
}

Access the getter and setter of a typescript property

I have a question about typescript properties: Is it possible to get the setter and getter of a typescript property or to declare a function argument to be of a property of X type?
The reason is to get some sort of "reference" to a variable which is not possible in plain JS without writing getter/setter wrappers or access the variable via parent object itself (obj["varname"]).
For example (with some working code and other parts speculative):
//A sample class with a property
class DataClass<T> {
private T val;
public get value(): T {
return this.val;
}
public set value(value: T) {
this.val = value;
}
}
//Different ways of modifing a member "by reference"
class ModifyRef {
public static void DoSomethingByGetterAndSetter(getter: () => string, setter: (val: string) => void) {
var oldValue = getter();
setter("new value by DoSomethingByGetterAndSetter");
}
public static void DoSomethingByObject(obj: Object, name: string) {
var oldValue = obj[name];
obj[name] = "new value by DoSomethingByObject";
}
//Is something like this possible?
public static void DoSomethingByProperty(somePropery: property<string>) {
var oldVlaue = someProperty;
someProperty = "new value by DoSomethingByProperty";
}
}
var inst = new DataClass<string>();
//Calling the DoSomethingByProperty if possible
ModifyRef.DoSomethingByProperty(inst.value);
//Or if not is something like this possible
ModifyRef.DoSomethingByGetterAndSetter(inst.value.get, inst.value.set);
The simplest way to do this would be to provide methods, rather than a property:
//A sample class with a property
class DataClass<T> {
private val: T;
public getValue(): T {
return this.val;
}
public setValue(value: T) {
this.val = value;
}
}
class ModifyRef {
public static DoSomethingByGetterAndSetter(getter: () => string, setter: (val: string) => void) {
var oldValue = getter();
setter("new value by DoSomethingByGetterAndSetter");
}
}
var inst = new DataClass<string>();
//Or if not is something like this possible
ModifyRef.DoSomethingByGetterAndSetter(inst.getValue, inst.setValue);
I've long found it very surprising that languages with properties don't include a convenient way to make a reference to a property, and have daydreamed about having this feature in C#. It ought to work on local variables as well.
A popular pattern for this kind of first-class or reified property is a single function that can be called in two ways:
no arguments: returns current value.
one argument: sets value, returns undefined.
Or in TypeScript terms:
interface Property<T> {
(): T;
(newVal: T): void;
}
The methods of jQuery objects often work like this. An example of this pattern in modelling pure data is in Knockout, in which such properties also support change subscriptions, and there's a rather elegant pattern for defining computed properties that automatically recompute when their dependencies change.

Swift property override not working

When I try to override a property I get an error "can not override mutable property with read-only property"
I have provided get and set in the super class.
class Card {
var contents:String {
get {
return self.contents
}
set {
self.contents = newValue
}
}
init() {
self.contents = ""
}
}
Here is my Subclass where I am trying to override the "contents" property.
class PlayingCard: Card {
override var contents:String { //<-- this is where I get the build error
get {
var rankStrings:Array<String> = PlayingCard.rankStrings()
return rankStrings[Int(self.rank)] + self.suit
}
}
}
What exactly am I doing wrong?
If the property you're overriding has both a getter and a setter, you need to provide both in your subclass as well. Here's the relevant part from the Swift language guide (emphasis mine):
You can present an inherited read-only property as a read-write
property by providing both a getter and a setter in your subclass
property override. You cannot, however, present an inherited
read-write property as a read-only property.
If you're not doing anything special with the value, then you'll typically want to pass the value being set on to the base class:
set {
super.contents = newValue
}
You could also just discard the value with an empty setter (although I can't think of a good reason to do this offhand):
set { }
I also wanted to point out that you have an infinite loop in the contents property in your Card class. When you you do this:
get {
return self.contents
}
You're actually just calling that same getter again, creating an infinite loop; you're doing the same with the setter. Swift doesn't create ivars for your properties automatically like Objective-C did, so you need to create them yourself. A more appropriate way to create that property would be to do something like this:
class Card {
private var _contents: String
var contents: String {
get {
return _contents
}
set {
_contents = newValue
}
}
init() {
_contents = ""
}
}
However, since you're not doing anything other than setting and returning _contents in your setter and getter, you can simplify it down to this:
class Card {
var contents: String = ""
init() {
}
}
Note: contents might also be a good candidate for using an optional (String?) and setting it to nil rather than initializing it to an empty string.
The compiler error message is fairly straightforward: Card's contents property is mutable, which is to say it has a set method in addition to the get method.
Your override only adds a get method, you need to add a set method too.
I think this is what you want:
set(newValue) {
rankStrings[Int(self.rank)] = newValue;
}