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

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()
}
}

Related

class property becomes null after viewModel calls it in kotlin

class MainAcitvity
fun roomSetup(){
setFavouriteDao = FavouriteDatabase.getDatabase(applicationContext).setFavouriteDao()
repositoryRoom = LorRepository(setFavouriteDao)
viewModelRoom = ViewModelProvider(this,LorViewModelFactory(repositoryRoom!!)).get(LorViewModel::class.java)
}
override fun onMovieClick(position: Int) {
roomSetup()
Toast.makeText(this#MainActivity, "clicked!"+position, Toast.LENGTH_SHORT).show()
var setFavourite = SetFavourite(movieResponse!!.docs[position].Id.toString(),movieResponse!!.docs[position].name.toString())
viewModelRoom.addToFavourites(setFavourite)
}
class ViewModel
fun addToFavourites(setFavourite: SetFavourite){
viewModelScope.launch(Dispatchers.IO){
lorRepository.addToFavourites(setFavourite)
}
}
class LorRepository( favouriteDao: SetFavouriteDao?) {
var favouriteDao : SetFavouriteDao
init {
this.favouriteDao = favouriteDao!!
}
private var lorApi: LORApi.LorCalls? = null
constructor(lorApi2 : LORApi.LorCalls?, favouriteDao: SetFavouriteDao?) : this(favouriteDao){
this.lorApi = lorApi2
}
I have 2 constructors
one to initialize room other for initializing retrofit
I am Also doubtful about the constructor in Repository. Thoose are made for 2 different purposes, one for initializing room database and other for repository. but everytime I create one object of room/retrofit the second constructor , when called, fills it with null values
My questions for you are:
Why do you to initialize retrofit and room's dao in a separate constructor?
What is it that you try to achieve?
In your code you only call to initialize dao constructor, therefore lorApi is null.
For your case you wouldn't want to initialize them separately.
Change your code to this:
class LorRepository(private val lorApi : LORApi.LorCalls, private val favouriteDao: SetFavouriteDao)

In Kotlin, can I apply lint to the constructors of all classes that inherit the class I specify?

class SomeDetector : Detector(), SourceCodeScanner {
override fun getApplicableConstructorTypes(): List<String>? {
return listOf(PARENT_CLASS)
}
override fun visitConstructor(context: JavaContext, node: UCallExpression, constructor: PsiMethod) {
// blabla...
}
}
Ok, I've even succeeded in applying lint to individual constructors for each class.
However, there are hundreds of classes I want to validate, and they all inherit a common interface.
So I want to verify the constructors of all classes inheriting the interface I specified.
The class I want to verify has an android dependency, so libraries like reflections cannot be used directly in the lint module, which is a java-library.
Can you help me to meet my requirements?
I gave your problem a go. I have checked if the value in annotation and the argument used are equal. You can tweak the code to suit your requirement. Here is a sample Detector class wherein I have provided explanations using comments. You can improve on it.
class InvalidConstructorCallDetector : Detector(), Detector.UastScanner {
// Check for call expressions
override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)
override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
override fun visitCallExpression(node: UCallExpression) {
// Check if call is constructor call and if the class referenced inherits your interface
if (node.isConstructorCall() &&
context.doesInherit(node, "com.example.android.YourInterface")
) {
val constructor = node.resolve()!!
// Get the first parameter. You may use a loop and check for all parameter or whatever you require
val param = constructor.parameterList.parameters[0]
// Get your annotation
val paramAnnotation =
param.getAnnotation("com.example.android.YourAnnotation")!!
// Get the value you specified in the annotation for the constructor declaration
val attributeValue = paramAnnotation.findAttributeValue("value")!!.text.toInt()
// Get the argument used for first parameter. Again, you can have this in a loop and use index
val argumentValue = node.getArgumentForParameter(0)!!.evaluate().toString().toInt()
if (attributeValue != argumentValue) // Checking for equality. Perform whatever check you want
{
context.report(
ISSUE,
node,
context.getNameLocation(node),
"Argument value($argumentValue) is invalid. Valid argument: $attributeValue"
)
}
}
}
}
// Check to see if class referenced by constructor call implements interface
private fun JavaContext.doesInherit(node: UCallExpression, typeName: String): Boolean {
for (type in node.classReference!!.getExpressionType()!!.superTypes) {
if (evaluator.typeMatches(type, typeName)) return true
}
return false
}
companion object {
val ISSUE = Issue.create(
"InvalidConstructorCall",
"Invalid arguments in constructor call",
"Only values defined in annotation in constructor declaration are allowed",
Category.CORRECTNESS,
10,
Severity.ERROR,
Implementation(
InvalidConstructorCallDetector::class.java,
EnumSet.of(Scope.JAVA_FILE)
)
)
}
}

How do I initialize a collection value for a MutableLiveData object?

class Foo : ViewModel() {
val bars: MutableLiveData<ArrayList<Bar>> = MutableLiveData()
get() {
if(field.value == null) {
field.setValue(ArrayList()) // NullPointerException
}
}
}
class FooTest(){
#Test fun itShouldNotBlowUp() {
Foo() //nullPointerException
}
}
I don't understand how to initialize the value of a MutableLiveData object. I've tried to lazily initialize it via the getter and with an init block. Both approaches throw a null pointer when setting the value. bars is not null however.
Here is the stacktrace:
java.lang.NullPointerException
at android.arch.core.executor.DefaultTaskExecutor.isMainThread(DefaultTaskExecutor.java:58)
at android.arch.core.executor.ArchTaskExecutor.isMainThread(ArchTaskExecutor.java:116)
at android.arch.lifecycle.LiveData.assertMainThread(LiveData.java:434)
at android.arch.lifecycle.LiveData.setValue(LiveData.java:279)
at android.arch.lifecycle.MutableLiveData.setValue(MutableLiveData.java:33)
at org.Foo.<init>(Foo.kt:10)
at org.FooTest.ShouldNotBlowUp(FooTest.kt:3)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
How do I initialize the ArrayList and set it as the value of bars?
Maybe the reason is that you are trying to run this in the test. Your sample fails in DefaultTaskExecutor.isMainThread(), that looks like this:
public boolean isMainThread() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
So Looper.getMainLooper() returns null in the test environment.
And also, have you tried to initialize property via 'lazy' delegate?
val bars: MutableLiveData<List<Bar>> by lazy {
MutableLiveData<List<Bar>>().apply {
value = emptyList()
}
}
That works fine in my case and feels more idiomatic
The answer to the question is: if you want to give it an empty value clearly, but the variable is not null and you can at least use it without breaking the program.
to create a variable:
val myVariable = MutableLiveData<ArrayList<String>>()
Well, now you only have to pass the constructor of the empty list within the type parentheses so that your variable does not remain with a Null value.
val myVariable = MutableLiveData<ArrayList<String>>(arrayListOf())
I know it's an old question but in case it can help someone, it helped me discover this ;)

How to "lock" static object in Kotlin

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)
}
}

Unresolved reference inside anonymous Kotlin listener

I have the code below. It is Kotlin. Any idea why textToSpeech from textToSpeech.setLanguage(Locale.UK) is telling that there is no reference resolved for textToSpeech?
val textToSpeech = TextToSpeech(
applicationContext,
object : TextToSpeech.OnInitListener {
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
textToSpeech.setLanguage(Locale.UK)
}
}
})
At first I assumed it is an Idea kotlin plugin bug, but it seems that it actually can't be compiled
Kotlin has hardened the variables initialization policy, and it's now prohibited to reference the variable inside its initializer, even in lambdas and object expressions, which seems reasonable: imagine that a lambda is called immediately before the variable assignment.
For your case, I can suggest as a workaround using an object expression in this quite cumbersome construct:
val textToSpeech = object {
val value: TextToSpeech get() = inner
private val inner = TextToSpeech(
applicationContext,
{ value.setLanguage(Locale.UK) }
)
}.value
This will initialize an anonymous object with inner inside that is acceptable through value property. Note that the inner initializer uses value property. Then the value is extracted and can be used.
But please keep in mind that this trick is unsafe: in runtime, using value before inner is assigned (e.g. in TextToSpeech constructor) will throw NullPointerException.
Also, I've replaced the OnInitListener with a lambda using SAM conversion to be short, but object expression can still be used there.
UPD: check this question for my effort to generalize this approach. Using it, you can write
val textToSpeech = selfReference {
TextToSpeech(
applicationContext,
{ self.setLanguage(Locale.UK) }
)
}
See the sources on Github.
This is a very readable and clear way to face that problem. First you should define this:
fun <T> selfReferenced(initializer: () -> T) = initializer.invoke()
operator fun<T> T.getValue(any: Any?, property: KProperty<*>) = this
and later use
val valueName: ValueType by selfReferenced{
//here you can create and use the valueName object
}
Using your problem as example you can do:
val textToSpeech:TextToSpeech by selfReferenced {
TextToSpeech(
applicationContext,
TextToSpeech.OnInitListener { status ->
if (status == TextToSpeech.SUCCESS) {
textToSpeech.setLanguage(Locale.UK)
}
})
}
Inside the selfReferenced block you can use the outer object with no restrictions. The only thing you should take care of, is declaring the type explicitly to avoid recursive type checking issues.

Categories

Resources