Let you have some variable and apply, run, let, also, takeIf or with function:
// private lateinit var someAdapter: SomeAdapter
recycler_view.apply {
this.layoutManager = LinearLayoutManager(context)
if (this::someAdapter.isInitialized) { // Compilation error.
this.adapter = someAdapter
}
}
How to access a value or state of someAdapter?
You can use one of the following.
recycler_view.apply {
this.layoutManager = LinearLayoutManager(context)
if (this#OuterClass::someAdapter.isInitialized) { //qualified
this.adapter = someAdapter
}
}
Or
recycler_view.apply {
this.layoutManager = LinearLayoutManager(context)
if (::someAdapter.isInitialized) { // qualified works for immediate outer scope
this.adapter = someAdapter
}
}
You can access it via this#YourClass.adapter. It's called qualified this, you can have a look at the documentation here
Not direct answer, but rather design opinion: lateinit vars are supposed to be used when you're guaranteed to have them set before use.
isInitialized was not even present before Kotlin 1.2, it was only added (opinion ahead) as an error fallback.
If you're getting notInitializedException thats good indicator you're not properly initializing the field, if you explicitly initialize it later then you should use nullable field instead: SomeAdapter?.
Accessing the outer this is possible via this#OuterClass. But, if you do not have any other someAdapter you can also just omit the this#YourOuterClass and simply use the following instead:
if (::someAdapter.isInitialized) {
So given the following, it is clear that someVar of Outer is meant:
class Outer<T> where T : Any {
lateinit var someVar : T
inner class Inner {
fun isItInitialized() = ::someVar.isInitialized
}
}
Given the following however:
class Outer<T> where T : Any {
lateinit var someVar : T
inner class Inner {
lateinit var someVar : T
fun isItInitialized() = ::someVar.isInitialized // now someVar of Inner is used
}
}
you need to specify this#Outer::someVar.isInitialized if you want to access the outer someVar.
Related
I am trying to observe the LiveData for the method which returns custom pair having 2 values. I want the observable to be triggered when I change either one of the values. But it is not getting triggered. Following is the code:
CustomPair.kt
data class CustomPair<T, V>(
var first : T,
var second : V
)
Observable:
falconViewModel.getPlanet1Name().observe(this) {
planet1.text = it.first
planet1.isEnabled = it.second
}
Getter and setter methods in ViewModel falconViewModel
private val planet1EnabledAndText = MutableLiveData<CustomPair<String, Boolean>>()
fun getPlanet1Name() : LiveData<CustomPair<String, Boolean>> {
return planet1EnabledAndText
}
fun setPlanet1Name(planetName : String, visibility : Boolean) {
planet1EnabledAndText.value?.run {
first = planetName
second = visibility
}
}
Can't we observe the value in such case? Please help what is wrong here.
It started working when I tried to set a new value of CustomPair instead of modifying the existing values in the object.
Replaced
planet1EnabledAndText.value = CustomPair(planetName, visibility)
with
planet1EnabledAndText.value?.run {
first = planetName
second = visibility
}
and it worked.
I am trying to create an ArrayList in a class, like so:
class ConvertableTests : BaseTest(){
var categories = ArrayList<String>()
categories.add('a') <---- //Expecting member declaration error here
inner class ConvertableClass : Convertible...
Why I can't add objects to my array list after I initialize the list?
You can add items into the list after you initialize the list if you aren't doing so at the root scope of the class. Same as if you would have tried to do the same thing in Java.
i.e.
//this won't work, like you just found out
class Example {
var categories = ArrayList<String>()
categories.add("a") // this isn't inside a function or an `init` block
}
You need to put it inside of a function or an init block
fun functionExample() {
var categories = ArrayList<String>()
categories.add("a") // This would work fine
}
or
class Example {
var categories = ArrayList<String>()
init {
categories.add("a")
}
}
To elaborate on Sergey's example of the apply and why that works if you don't do it inside of a function or an init
class Example {
var categories = ArrayList<String>().apply {
add("a")
}
}
The kotlin compiler is performing an optimization and it's actually treating this as if you were putting it into an init block. If you decompile this and see what's happening, it's actually doing this
/// This is what it compiles back up to in Java
public Example() {
ArrayList var8 = new ArrayList();
var8.add("a");
this.category = var8;
}
Which is the same thing that happens when you use the init block.
Hope that helps!
You can use init block to initialize array:
class ConvertableTests : BaseTest() {
var categories = ArrayList<String>()
init {
categories.add("a")
}
// ...
}
Or apply extension function:
var categories = ArrayList<String>().apply {
add("a")
}
Also you should use double quotes " to add a String:
var categories = ArrayList<String>()
categories.add("a")
Single quotes are used for Chars:
var categories = ArrayList<Char>()
categories.add('a')
I have this code using Kotlin:
class A{
var myVariable:String?=null
constructor(myVariable:String){
this.myVariable = myVariable
}
init {
println("Success !")
}
inner class B{
init {
println(myVariable)
}
}
}
fun main(args:Array<String>){
var b = A("test").B // this does not work
}
And unfortunately it does not work I get:
Error:(20, 23) Kotlin: Nested class 'B' accessed via instance reference
Error:(20, 23) Kotlin: Classifier 'B' does not have a companion object, and thus must be initialized here
How can I solve my problem?
When you do:
A("test").B
You're saying "Fetch something from an instance of A" - in this case, it's a class. However, this isn't the syntax you're looking for in your case. You can get mostly anything, but getting a reference is a separate issue. If you want to get a function, the syntax is different from getting a field or calling a function. Although this isn't very important, bit might still be worth keeping that in mind.
Since B is an inner class, you're so far entirely correct that you need an instance of A first. But you also need an instance of B. When you initialize B, it's still "connected" to the parent class, which is why you can access outer variables without any problems. However, it's still an initializable class - and you need to initialize it.
So you need to initialize B as well. The Kotlin syntax for this is pretty nice (where as the syntax in Java is slightly horrible) - all you need to add is () at the end.
So you'll end up with this:
val b = A("test").B()
// ...
This is because it's an inner class. If you had a static inner class (in Kotlin, that's a class within a class without the inner keyword), the initialization would've been A.B() - A isn't initialized in those cases.
You can also split up the initialization:
val a = A("test")
val b = a.B();
Once you have the variable, it's exactly like every other variable - the only difference here is the initialization.
Try this:
class A{
var myVariable:String?=null
constructor(myVariable:String){
this.myVariable = myVariable
}
init {
println("Success !")
}
inner class B{
init {
println(myVariable)
}
}
}
fun main(args:Array<String>){
var b = A("test").B() // You have to call the constructor here
}
You should create an instance (call constructor) of class B before accessing its members:
fun main(args:Array<String>) {
val b = A("test").B()
b.someFunction()
}
class A {
var myVariable:String? = null
constructor(myVariable: String) {
this.myVariable = myVariable
}
init {
println("Success !")
}
inner class B {
init {
println(myVariable)
}
fun someFunction() {
myVariable = "set new value to the variable"
}
}
}
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 :-)
I wonder if there is a way to check if a lateinit variable has been initialized. For example:
class Foo() {
private lateinit var myFile: File
fun bar(path: String?) {
path?.let { myFile = File(it) }
}
fun bar2() {
myFile.whateverMethod()
// May crash since I don't know whether myFile has been initialized
}
}
There is a lateinit improvement in Kotlin 1.2 that allows to check the initialization state of lateinit variable directly:
lateinit var file: File
if (this::file.isInitialized) { ... }
See the annoucement on JetBrains blog or the KEEP proposal.
UPDATE: Kotlin 1.2 has been released. You can find lateinit enhancements here:
Checking whether a lateinit var is initialized
Lateinit top-level properties and local variables
Using .isInitialized property one can check initialization state of a lateinit variable.
if (::file.isInitialized) {
// File is initialized
} else {
// File is not initialized
}
You can easily do this by:
::variableName.isInitialized
or
this::variableName.isInitialized
But if you are inside a listener or inner class, do this:
this#OuterClassName::variableName.isInitialized
Note: The above statements work fine if you are writing them in the same file(same class or inner class) where the variable is declared but this will not work if you want to check the variable of other class (which could be a superclass or any other class which is instantiated), for ex:
class Test {
lateinit var str:String
}
And to check if str is initialized:
What we are doing here: checking isInitialized for field str of Test class in Test2 class.
And we get an error backing field of var is not accessible at this point.
Check a question already raised about this.
Try to use it and you will receive a UninitializedPropertyAccessException if it is not initialized.
lateinit is specifically for cases where fields are initialized after construction, but before actual use (a model which most injection frameworks use).
If this is not your use case lateinit might not be the right choice.
EDIT: Based on what you want to do something like this would work better:
val chosenFile = SimpleObjectProperty<File?>
val button: Button
// Disables the button if chosenFile.get() is null
button.disableProperty.bind(chosenFile.isNull())
If you have a lateinit property in one class and need to check if it is initialized from another class
if(foo::file.isInitialized) // this wouldn't work
The workaround I have found is to create a function to check if the property is initialized and then you can call that function from any other class.
Example:
class Foo() {
private lateinit var myFile: File
fun isFileInitialised() = ::file.isInitialized
}
// in another class
class Bar() {
val foo = Foo()
if(foo.isFileInitialised()) // this should work
}
This will work
if (::list.isInitialized) {
//true
}
else {
//false
}
Accepted answer gives me a compiler error in Kotlin 1.3+, I had to explicitly mention the this keyword before ::. Below is the working code.
lateinit var file: File
if (this::file.isInitialized) {
// file is not null
}
Checking lateinit var
To check whether a lateinit var was initialized or not, simply use an .isInitialized boolean on the property reference :: .
if (foo::bar.isInitialized) {
println(foo.bar)
}
Playground Kotlin's code may look like this:
fun main() {
var declarative = Declarative()
declarative.checkLateInit()
}
class Declarative {
lateinit var compose: String
fun checkLateInit() {
println(this::compose.isInitialized)
compose = "Jetpack Compose 1.2"
if (this::compose.isInitialized) {
println(this.compose)
}
}
}
// Result:
// false
// Jetpack Compose 1.2
This checking is only available for the properties that are accessible lexically, i.e. declared in the same type or in one of the outer types, or at top level in the same file.
kotlin.UninitializedPropertyAccessException: lateinit property clientKeypair has not been initialized
Bytecode says...blah blah..
public final static synthetic access$getClientKeypair$p(Lcom/takharsh/ecdh/MainActivity;)Ljava/security/KeyPair;
`L0
LINENUMBER 11 L0
ALOAD 0
GETFIELD com/takharsh/ecdh/MainActivity.clientKeypair : Ljava/security/KeyPair;
DUP
IFNONNULL L1
LDC "clientKeypair"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.throwUninitializedPropertyAccessException (Ljava/lang/String;)V
L1
ARETURN
L2
LOCALVARIABLE $this Lcom/takharsh/ecdh/MainActivity; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
Kotlin creates an extra local variable of same instance and check if it null or not, if null then throws 'throwUninitializedPropertyAccessException' else return the local object.
Above bytecode explained here
Solution
Since kotlin 1.2 it allows to check weather lateinit var has been initialized or not using .isInitialized