How to "lock" static object in Kotlin - android

Well, I've a situation, where in Class A I get "X DATA".
I want to store this "X DATA" in Object X one time and then make sure, the values of this object is not possible to change. (Set it once and forget about it).
My approach:
object X {
var attribute1: String
var attribute2: String
}
Obviously, as object attributes are var they are changeable in future. How could I avoid this? Is there a way to assign values (in some time..) and then lock the object till application is exited?

You could use a delegate property
class MyProperty<T : Any> {
private var value: T? = null
operator fun getValue(myObject: MyObject, property: KProperty<*>): T =
value ?: throw UninitializedPropertyAccessException()
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = when (this.value) {
null -> this.value = value
else -> throw IllegalAccessException("Property already initialized")
}
}
and then use var in your object
object MyObject {
var myProperty by MyProperty<String>()
}
In the sample above, if you try to access myProperty before setting a value, an exception is thrown, but you could handle that as you wish (return a default value? maybe null?).
If you try to assign the value more than once, you get an exception as well but you could handle that differently, for instance, by simply not setting the value anymore so that
MyObject.myProperty = "foo"
MyObject.myProperty = "bar"
println(MyObject.myProperty)
will print "foo"

You can use lateinit var attribute1: String to tell the compiler that you will manage setting attribute1 to a non-null value before it's used.
There's no such thing as lateinit val to "lock" the value as you say.
The docs have more information.

I suggest you make those properties final by replacing var with val.
var is like general variable and its known as a mutable variable in kotlin and can be assigned multiple times.
val is like constant variable and its known as immutable in kotlin and can be initialized only single time.

You can't lateinit immutable properties in kotlin and lateinit var does not allow custom setters.
So my approach would be implementing the lateinit var behavior with a backing property, custom getter and custom setter. This is quite a similar approach to lellomans solution.
object X {
private lateinit var backingprop: String
var attribute: String
set(arg) {
if (this::backingprop.isInitialized) throw IllegalAccessException("Property already initialized")
backingprop = arg
}
get() = backingprop
}

I warn you! But you can use such example:
object Immutable {
val immutableString: String = mutableStaticString ?: "some default value, just in case"
}
var mutableStaticString: String? = null
class App : Application() {
override fun onCreate() {
super.onCreate()
mutableStaticString = "hello duct tape solutions!"
android.util.Log.d("ductTape", mutableStaticString)
android.util.Log.d("ductTape", Immutable.immutableString)
}
}

Related

Reference an object in a class by using a string?

I want to reference an object within this class I have below:
class HerbData {
object Dill {
const val herbName: String = "This is Dill!"
const val scientificName: String = "Anethum Graveolens"
val dullThumbnail: Int = R.drawable.dill_thumbnail_attr
}
object Peppermint {
val herbName: String = "This is Peppermint!"
}
}
Is there anyway that I can reference the object by using a string in Kotlin? Here is somewhat what I mean:
HerbData."Dill".herbname
I can't find anything on this topic for Kotlin.
Another way you could do this is with an enum class. The advantage over a map is that you have a data structure you can reference directly in code, so you could use HerbData.Dill as well as HerbData["Dill"]. And that will enable you to take advantage of compile-time checking and lint warnings, refactoring, exhaustive pattern matching, code completion etc, because the data is defined in your code
enum class HerbData(
val herbName: String,
val scientificName: String? = null,
val dullThumbnail: Int? = null
) {
Dill("This is Dill!", "Anethum Graveolens", R.drawable.dill_thumbnail_attr),
Peppermint("This is Peppermint!");
companion object {
operator fun get(name: String): HerbData? =
try { valueOf(name) } catch(e: IllegalArgumentException) { null }
}
}
fun main() {
// no guarantee these lookups exist, need to null-check them
HerbData["Peppermint"]?.herbName.run(::println)
// case-sensitive so this fails
HerbData["peppermint"]?.herbName.run(::println)
// this name is defined in the type system though! No checking required
HerbData.Peppermint.herbName.run(::println)
}
>> This is Peppermint!
null
This is Peppermint!
Enum classes have that valueOf(String) method that lets you look up a constant by name, but it throws an exception if nothing matches. I added it as a get operator function on the class, so you can use the typical getter access like a map (e.g. HerbData["Dill"]). As an alternative, you could do something a bit neater:
companion object {
// storing all the enum constants for lookups
private val values = values()
operator fun get(name: String): HerbData? =
values.find() { it.name.equals(name, ignoreCase = true) }
}
You could tweak the efficiency on this (I'm just storing the result of values() since that call creates a new array each time) but it's pretty simple - you're just storing all the enum entries and creating a lookup based on the name. That lets you be a little smarter if you need to, like making the lookup case-insensitive (which may or may not be a good thing, depending on why you're doing this)
The advantage here is that you're generating the lookup automatically - if you ever refactor the name of an enum constant, the string label will always match it (which you can get from the enum constant itself using its name property). Any "Dill" strings in your code will stay as "Dill" of course - that's the limitation of using hardcoded string lookups
The question really is, why do you want to do this? If it's pure data where no items need to be explicitly referenced in code, and it's all looked up at runtime, you should probably use a data class and a map, or something along those lines. If you do need to reference them as objects within the code at compile time (and trying to use HerbData."Dill".herbName implies you do) then an enum is a fairly easy way to let you do both
Declare a Data Class
data class HerbData (
val scientificName: String,
val dullThumbnail: Int
)
Initialize a muteable map and put data in it
val herbData = mutableMapOf<String, HerbData>()
herbData.put("Dill", HerbData("Anethum Graveolens", R.drawable.dill_thumbnail_attr))
herbData.put("Peppermint", HerbData("Mentha piperita", R.drawable.peppermint_thumbnail_attr))
You can now just
herbData["Dill"]?.scientificName
class HerbData {
interface Herb {
val herbName: String
val scientificName: String
}
object Dill : Herb {
override val herbName: String = "This is Dill!"
override val scientificName: String = "Anethum Graveolens"
}
object Peppermint: Herb {
override val herbName: String = "This is Peppermint!"
override val scientificName: String = "Mentha piperita"
}
companion object {
operator fun get(name: String): Herb? {
return HerbData::class
.nestedClasses
.find { it.simpleName == name }
?.objectInstance as? Herb
}
}
}
println(HerbData["Dill"]?.herbName) // Prints: This is Dill!
println(HerbData["Peppermint"]?.scientificName) // Prints: Mentha piperita
println(HerbData["Pepper"]?.herbName) // Prints: null

What is the difference between the = operator and the reserved word by when assigning a remeber in jetpack compose?

I would like to know the difference between:
var textFieldState = remember {
mutableStateOf("")
}
and
var textFieldState by remember {
mutableStateOf("")
}
Is there any advantage over the other?
Is there any advantage over the other?
The first really should be a val and not a var. Otherwise, they are equivalent. Or, to quote the documentation:
There are three ways to declare a MutableState object in a composable:
val mutableState = remember { mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
val (value, setValue) = remember { mutableStateOf(default) }
These declarations are equivalent, and are provided as syntax sugar for different uses of state. You should pick the one that produces the easiest-to-read code in the composable you're writing.
In those three:
In the first, mutableState holds a MutableState, and you use .value and .value= to manipulate the contents
In the second, value holds a MutableState, but the by syntax tells the compiler to treat it as a property delegate, so we can pretend that value just holds the underlying data
In the third, a destructuring declaration gives you getter and setter references to manipulate the content in the underlying MutableState
The by in this context is a kotlin property delegate. Any class that implements the operator fun operator fun getValue(thisRef: Any?, prop: KProperty<*>): T can use this syntax. Using = will eagerly assign the variable (importantly without delegation), the by will delegate to the operator function. The remember in this case is just a shortcut function to creating the Remember delgate that wraps the value you are creating inside the { ... } block.
A typical example is the kotlin Lazy<T> class : val myValue : Int by lazy { 1 }. If used with the by operator you will return the Int value, if used with = it will return Lazy<Int> as you have not used delegation.
It is also worth noting that delgates can be setters as well by using this operator fun : operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: T).

Val cannot be reassigned although I have a var (kotlin)?

I made a var extension property to data class like this:
var Element.bitmap: Bitmap?
get() = downloadImg(PLACE_HOLDER_URL) //Default
set(value) = this.bitmap.let {
it = value ?: return#let
}
However, the compiler complaining "Val cannot be reassigned" when I try to reassign "it" to the new value in this line:
it = value ?: return#let
I couldn't really understand why this happens?? I have a "var" bitmap property not "val", So what is the problem? And more importantly what is solution or alternative?
The variable inside the lambdas are immutable, the it there is a shallow copy of the variable bitmap.
You should be using the field to assign into the backing field (actual variable).
set(value) {
value?.let { field = it }
}
The let function creates a shallow copy of the variable, so that if a visible mutable variable (var) is changed by another thread then it can be safely used without risking mutation.
Example:
class Test {
var prop: Int? = 5
}
fun main() {
val test = Test()
thread {
Thread.sleep(100)
test.prop = null
}
if (test.prop != null) {
Thread.sleep(300) // pretend we did something
println(test.prop) // prints null even inside if check
}
}
To tackle these situations, a shallow copy is used such as with let which passes a immutable shallow copy of these.
class Test {
var prop: Int? = 5
}
fun main() {
val test = Test()
thread {
Thread.sleep(100)
test.prop = null
}
test.prop?.let { // `it` is a shallow copy, changes won't be reflected
Thread.sleep(300) // pretend we did something
println(it) // prints 5
}
}
Conclusion: it is not the actual variable itself, so changes won't reflect to the actual variable even if you would have been able to assign something to it.
Edit: Extension properties can't have backing field, extensions are literally getters and setters.
One thing you can do is to make a Map with a unique identifier in which you can store the values, but that might not be able to be garbage collected
Another thing you can do (which I recommend) is to use delegation
Delegation Example:
class ElementBitmapDelegate {
private var value: Bitmap? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): Bitmap {
return value ?: downloadImg(PLACE_HOLDER_URL).also { setValue(thisRef, property, it) }
// return value or if value is null return from downloadImg() and set it to value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, v: Bitmap?) {
v?.let { value = it } // if v is not null then set value to v
}
}
var Element.bitmap: Bitmap? by ElementBitmapDelegate()
In order to create a custom setter, you need to access to the backing field of the property through field and not it, like this:
set(value) = {
field = value
}

How to programically trigger notify on MutableLiveData change

I have a LiveData property for login form state like this
private val _authFormState = MutableLiveData<AuthFormState>(AuthFormState())
val authFormState: LiveData<AuthFormState>
get() =_authFormState
The AuthFormState data class has child data objects for each field
data class AuthFormState (
var email: FieldState = FieldState(),
var password: FieldState = FieldState()
)
and the FieldState class looks like so
data class FieldState(
var error: Int? = null,
var isValid: Boolean = false
)
When user types in some value into a field the respective FieldState object gets updated and assigned to the parent AuthFormState object
fun validateEmail(text: String) {
_authFormState.value!!.email = //validation result
}
The problem is that the authFormState observer is not notified in this case.
Is it possible to trigger the notification programically?
Maybe you can do:
fun validateEmail(text: String) {
val newO = _authFormState.value!!
newO.email = //validation result
_authFormState.setValue(newO)
}
You have to set the value to itself, like this: _authFormState.value = _authFormState.value to trigger the refresh. You could write an extension method to make this cleaner:
fun <T> MutableLiveData<T>.notifyValueModified() {
value = value
}
For such a simple data class, I would recommend immutability to avoid issues like this altogether (replaces all those vars with vals). Replace validateEmail() with something like this:
fun validateEmail(email: String) = //some modified version of email
When validating fields, you can construct a new data object and set it to the live data.
fun validateFields() = _authFormState.value?.let {
_authFormState.value = AuthFormState(
validateEmail(it.email),
validatePassword(it.password)
)
}

Kotlin property with getter. Can I not specify an initial value?

I want to create a singleton class, but unfortunately, Android needs Context for almost anything so I need it to create an instance. So I just assumed the user called init(), and then return the instance. As you see below, if the _instance is null, an exception will be thrown, so the get method cannot return null.
But Kotlin says I must initialise instance. The things is, that MyClass cannot be created without a context. So I would like not to specify an initial value. How can I do that?
companion object
{
protected var _instance:MyClass? = null;
fun init(context:Context)
{
_instance = MyClass(context)
}
var instance:MyClass //<---This causes a compile error.
get()
{
if(_instance==null) throw RuntimeException("Call init() first.");
return _instance!!;
}
}
Change the var to val and it should work:
....
val instance: MyClass
....
A variable property (var) not only assumes a getter, but also a setter. Since you provided no setter, a default one was generated set(value) { field = value }. Despite is uselessness in this situation, the default setter uses field, thus requires its initialization.
Use lateinit property
public class MyTest {
lateinit var subject: TestSubject
fun setup() {
subject = TestSubject()
}
fun test() {
subject.method()
}
}

Categories

Resources