- Issue
- Solution
- Kotlin – “Leaking ‘this’ in constructor” warning should apply to final classes as well as open ones
- Best Solution
- «Leaking ‘this’ in constructor» warning should apply to final classes as well as open ones?
- Related videos on Youtube
- Yoni Gibbs
- Comments
- [Solved]-«Leaking ‘this’ in constructor» warning should apply to final classes as well as open ones?-kotlin
- Related Query
- More Query from same tag
- Предупреждение «Утечка этого» в конструкторе должно относиться как к конечным классам, так и к открытым?
Issue
I have an abstract class that implements DefaultLifecycleObserver . I’d like to call lifecycle.addObserver(this) from the init block, but it says «Leaking ‘this’ in constructor of non-final class MyAbstractClass».
abstract class MyAbstractClass(protected val activity: AppCompatActivity) : DefaultLifecycleObserver < init < activity.lifecycle.addObserver(this) >. . . >
I can move this line of code to the init block of each final class that extends this abstract class, but I don’t like the idea, especially because I want to guarantee that each new class that will extend MyAbstractClass in the future will call it as well. Is there a better place to call this without creating a leak?
Solution
I suppose you could post your call so it only happens after the object is fully instantiated:
abstract class MyAbstractClass(protected val activity: AppCompatActivity) : DefaultLifecycleObserver < init < Handler(Looper.getMainLooper()).post < activity.lifecycle.addObserver(this) >> >
Or it might be less surprising to create an extension function you can tack onto your constructor calls. Then you can explicitly start the observation immediately. You’d have to make activity public, though. By defining it in an extension like this, your subclasses can call this and return themselves so you can chain it to constructor calls.
fun T.alsoBegin(): T < activity.lifecycle.addObserver(this) return this >val foo = SomeImplementation(myActivity).alsoBegin()
Kotlin – “Leaking ‘this’ in constructor” warning should apply to final classes as well as open ones
In Kotlin if you have an open class which refers to this in its constructor or init block, you (quite rightly) get a compiler warning:
Leaking ‘this’ in constructor of non-final class
The reason for this is explained here.
My question is: why is this not reported when the class is final? If this is used in the init block before that block has completed, the object is still not in a fully constructed state, so shouldn’t the warning apply there too?
This can even lead to a situation where a val property seems to change at runtime. Take this code as an example:
class Listener < fun onCreated(leaker: Leaker) = println("Listener hears that leaker created with a value of $") > class Leaker(listener: Listener) < val myVal: Int init < listener.onCreated(this) myVal = 1 println("Leaker knows that it's been created with a value of $myVal") >>
Using these objects as follows:
will result in the following output:
Listener hears that leaker created with a value of 0 Leaker knows that it's been created with a value of 1
Notice that myVal is initially reported as being 0, then as being 1.
As can be seen, Leaker passes an instance of itself to Listener before Leaker has been fully constructed. Listener can then access the myVal property before it’s been initialized, so it’ll have the default value (0 in this case as it’s an integer). Later on Listener then changes the value of this property (to 1 in this example). This means that the program behaves as if a val has changed.
Should the compiler warn you about this?
Best Solution
tl;dr: https://youtrack.jetbrains.com/issue/KT-22044 is a good fit regarding this issue.
I will cite what Intellij IDEAs inspection called «Leaking ‘this’ in constructor» says about this:
- Accessing a non-final property in constructor
- Calling a non-final function in constructor
- Using this as a function argument in a constructor of a non-final class
abstract class Base < val code = calculate() abstract fun calculate(): Int >class Derived(private val x: Int) : Base() < override fun calculate() = x >fun testIt() < println(Derived(42).code) // Expected: 42, actual: 0 >
I think that there should be warning nonetheless as you were able to access a non-initialized variable. The reason: the compiler already disallows direct access to uninitialized variables, i.e. the following will not compile:
but accessing it indirectly compiles and shows the default value of the variable type:
and there we also have a «leak», basically accessing some before it should 😉
«Leaking ‘this’ in constructor» warning should apply to final classes as well as open ones?
tl;dr: https://youtrack.jetbrains.com/issue/KT-22044 is a good fit regarding this issue.
I will cite what Intellij IDEAs inspection called «Leaking ‘this’ in constructor» says about this:
- Accessing a non-final property in constructor
- Calling a non-final function in constructor
- Using this as a function argument in a constructor of a non-final class
abstract class Base < val code = calculate() abstract fun calculate(): Int >class Derived(private val x: Int) : Base() < override fun calculate() = x >fun testIt() < println(Derived(42).code) // Expected: 42, actual: 0 >
I think that there should be warning nonetheless as you were able to access a non-initialized variable. The reason: the compiler already disallows direct access to uninitialized variables, i.e. the following will not compile:
but accessing it indirectly compiles and shows the default value of the variable type:
and there we also have a «leak», basically accessing some before it should 😉
Related videos on Youtube
Yoni Gibbs
Comments
In Kotlin if you have an open class which refers to this in its constructor or init block, you (quite rightly) get a compiler warning:
The reason for this is explained here. My question is: why is this not reported when the class is final? If this is used in the init block before that block has completed, the object is still not in a fully constructed state, so shouldn’t the warning apply there too? This can even lead to a situation where a val property seems to change at runtime. Take this code as an example:
class Listener < fun onCreated(leaker: Leaker) = println("Listener hears that leaker created with a value of $") > class Leaker(listener: Listener) < val myVal: Int init < listener.onCreated(this) myVal = 1 println("Leaker knows that it's been created with a value of $myVal") >>
Listener hears that leaker created with a value of 0 Leaker knows that it's been created with a value of 1
Notice that myVal is initially reported as being 0, then as being 1. As can be seen, Leaker passes an instance of itself to Listener before Leaker has been fully constructed. Listener can then access the myVal property before it’s been initialized, so it’ll have the default value (0 in this case as it’s an integer). Later on Listener then changes the value of this property (to 1 in this example). This means that the program behaves as if a val has changed. Should the compiler warn you about this?
[Solved]-«Leaking ‘this’ in constructor» warning should apply to final classes as well as open ones?-kotlin
tl;dr: https://youtrack.jetbrains.com/issue/KT-22044 is a good fit regarding this issue.
I will cite what Intellij IDEAs inspection called «Leaking ‘this’ in constructor» says about this:
- Accessing a non-final property in constructor
- Calling a non-final function in constructor
- Using this as a function argument in a constructor of a non-final class
abstract class Base < val code = calculate() abstract fun calculate(): Int >class Derived(private val x: Int) : Base() < override fun calculate() = x >fun testIt() < println(Derived(42).code) // Expected: 42, actual: 0 >
I think that there should be warning nonetheless as you were able to access a non-initialized variable. The reason: the compiler already disallows direct access to uninitialized variables, i.e. the following will not compile:
but accessing it indirectly compiles and shows the default value of the variable type:
and there we also have a «leak», basically accessing some before it should 😉
Related Query
- «Leaking ‘this’ in constructor» warning should apply to final classes as well as open ones?
- Kotlin calling non final function in constructor works
- This type has a constructor and must be initialized here — Kotlin
- kotlin how to refer outer-scope this in multi-layer apply functions
- java static final in kotlin: Const ‘val’ initializer should be a constant value
- Why are Kotlin classes final by default instead of open?
- Error: Getters of lazy classes cannot be final Kotlin Spring Boot
- Kotlin This Cursor should be freed up after use with #close
- Custom gradle task that accesses top-level fails with «The constructor . should be annotated with @Inject»
- Classes annotated with @Configuration could be implicitly subclassed and must not be final
- There are multiple good constructors and Room will pick the no-arg constructor. How to solve this warning
- Should Kotlin test classes be internal?
- Kotlin for android and static final inner classes
- How to fix ‘The feature «multi platform projects» is experimental and should be enabled explicitly’ warning in IntelliJ IDEA
- Accessing outer scope with qualified this in extension functions for inner classes
- Kotlin Class should have a single no-arg constructor
- JUnit with Kotlin — This class does not have a constructor
- Variables inside a constructor should be private or public in Kotlin
- KMM error: This API is internal in ktor and should not be used. It could be removed or changed without notice
- How to apply function to value defined in data class constructor before init method?
- Kotlin inheritance: pass this as constructor argument
- Unable to invoke no-args constructor for interface retrofit2.http.Url. Register an InstanceCreator with Gson for this type to fix this problem
- Kotlin: secondary constructor with this keyword
- mock final classes for both unit and UI tests
- kotlin compiler return this type of warning
- What should I do if I don’t want a devired class call base class’s constructor in Kotlin?
- Kotlin’s safe-cast (or in this context «unsafe»-cast) hides possible NullPointerException warning by Android Studio
- ValueAnimator Lint Error: This animation should be started with #start() [Recycle]
- should I write this code in my fragment/activity or in my viewmodel?
- Should I use FLAG_ACTIVITY_CLEAR_TOP in this case? how to use FLAG_ACTIVITY_CLEAR_TOP intent flag?
More Query from same tag
- Why retrofit returns null in response body?
- lateinit property not initialized when Activity is re-created
- Convert String obtained from edittext to Integer in Kotlin language
- Execution failed task ‘:app:kaptGenerateStubsDebugKotlin’
- How can I fix this issue with flutter unity widget?
- Kotlin n must be positive crash
- How to get the first value from a list without exceptions in Kotlin?
- Kotlin 1.5.2 data class hashcode( ) generated implementation will result in nullPointerExeption
- How to impose generic constraints to the parameters and return values of a function argument of a generic Kotlin function?
- How to set a SpannableString to an EditText in an Android Material TextInputLayout?
- Replacing GlobalScope.launch with something better
- Can comparison operator overloading in Kotlin be used to implement SQL like functionality?
- how to change listView Item content in Kotlin in an effecient way?
- How to use kotlinx.coroutines.withTimeout in kotlinx.coroutines.test.runTest?
- how to get item value from ListView setOnItemClickListener?
- AndroidX Room unresolved supertypes RoomDatabase
- LocationManager’s requestLocationUpdates() creates a notification
- Without AsyncTask, running a thread in background and updating the UI Thread
- how to convert elapsed time from now to that date and pass that method to viewholder in kotlin
- What’s the meaning of plus sign before a Kotlin method?
- Android — Status bar notification not showing
- How to write a method in Kotlin that will return a string from Firestore?
- spring data rest kotlin associations POST
- How do I ensure lateinit variable initialization before it is needed?
- how to reference an item from layer-list.xml in activity
- Can’t create secondary constructors in kotlin
- How to detect leaked Disposable subscriptions in RxJava code?
- Android R8 — Missing class: com.sun.javadoc.Doclet
- Multiplying 2 numbers without buttons in Kotlin TextWatcher
- how to specify maven compiler plugin order
Предупреждение «Утечка этого» в конструкторе должно относиться как к конечным классам, так и к открытым?
В Kotlin, если у вас есть класс open , который ссылается на this в своем конструкторе или блоке init , вы (совершенно справедливо) получите предупреждение компилятора:
Leaking ‘this’ in constructor of non-final class
Причина этого объясняется здесь.
Мой вопрос: почему этот нет сообщается, когда класс является окончательным? Если this используется в блоке init до того, как этот блок завершен, объект все еще не находится в полностью сконструированном состоянии, поэтому разве предупреждение не должно применяться и там?
Это может даже привести к тому, что свойство val изменится во время выполнения. В качестве примера возьмем этот код:
class Listener < fun onCreated(leaker: Leaker) = println("Listener hears that leaker created with a value of $") > class Leaker(listener: Listener) < val myVal: Int init < listener.onCreated(this) myVal = 1 println("Leaker knows that it's been created with a value of $myVal") >>
Использование этих объектов следующим образом:
приведет к следующему выводу:
Listener hears that leaker created with a value of 0 Leaker knows that it's been created with a value of 1
Обратите внимание, что myVal сначала отображается как 0, а затем как 1.
Как можно видеть, Leaker передает свой экземпляр Listener до того, как Leaker будет полностью сконструирован. Затем Listener может получить доступ к свойству myVal до его инициализации, поэтому оно будет иметь значение по умолчанию (в данном случае 0, поскольку это целое число). Позже Listener изменяет значение этого свойства (в этом примере на 1). Это означает, что программа ведет себя так, как если бы val был изменен.
Должен ли компилятор вас об этом предупредить?