Kotlin: difference between IT and THIS keyword - android

In one of the kotlin interviews, someone asked me the difference between it & this keywords.
I have a search on google but unable to find a proper answer for the question.
Can someone guide me what's the actual difference between these two?
I know this is very basic question, I am a novice in the kotlin.

it is only relevant inside a lambda with a single parameter. It is the default name for a single parameter and is a shorthand that allows you to omit naming the single parameter. A function that is declared this way might look like this:
(String) -> Unit
In a lambda, this is the receiver argument. It only works if the function is defined as having a receiver, like this:
String.() -> Unit
If the function declaration does not have a receiver, this has the same meaning it does outside the scope of the lambda. For an extension function, that’s the receiver of the extension function. Otherwise, it’s the class containing the function.

You need to know about Scope Functions:
The Kotlin standard library contains several functions whose sole
purpose is to execute a block of code within the context of an object.
When you call such a function on an object with a lambda expression
provided, it forms a temporary scope.
Inside this scope there is a Context object either as this or it
In Scope functions run, apply and with the scope is (temporarily) changed to the scope of the object you are calling this function on:
val str = "Hello"
str.run {
//Here this refers to str
}
In Scope functions let, also the scope is not changed (remains the same as caller scope) but your lambda will receive the context as it inside the lambda:
val str = "Hello"
str.let {
//Here it refers to str
}
You can check the links for more information.

Difference between it & this keywords can be explained by taking example of lambda method receivers (a.k.a higher order functions).
Let's say you've written a function or using a function which provides you callback as lambda method receiver. Something like this: () -> Unit
So, there are two possibilities how you want your callback to be:
Providing parameter to callback
Parameter by callback means you want to give your callback a parameter that caller can use on the time of invocation, also considered as it.
Whatever written above simply means: (Int) -> Unit. this functional method parameter can give you integer at the time of invocation.
Check out the snippet below:
fun someMethodWithCallback(callback: (Int) -> Unit) {
callback(0)
}
// On the time of consumption, the `Int` parameter by default exposed to callback as it parameter.
obj.someMethodWithCallback { it -> // Here it is the method parameter of callback that we passed, you can also rename it to any other named value
// it can be directly used as Int value if needed or you can rename it at receiver above
}
Note: You can provide multiple parameters to callback and then you won't be able to receive it, rather callback would provide you number of variables passed instead.
Providing object to callback
Another way to provide callback is by providing object itself as callback parameter. Which means that callback syntax slightly changes and gives you object itself as callback parameter this.
Whatever written above simply means: Int.() -> Unit. this functional method object can give you integer at the time of invocation.
Check out the snippet below:
fun someMethodWithCallback(callback: Int.() -> Unit) {
callback(0)
}
// On the time of consumption, the `Int` parameter by default exposed to callback as it parameter.
obj.someMethodWithCallback { this // Here this is the method object of callback that we passed, you can not rename it to anything else
// it can be used as Int value by referencing as this
}
Hope it make sense!

If this helps some one :
With scope like : run, apply, with, to call the methods of the object, we can call them directly as it has scope of the object. In another words run-block has "this" scope.
private fun methodWithRun() {
val dummy = Dummy()
dummy.run {
this.dummyFun()
this.dummyVar = "10"
}
}
While scope like : let, also, to call the methods of the object, we can call them using the "it" as it has scope of the class win which this method is written.
private fun methodWithLet() {
val dummy = Dummy()
dummy.let {
it.dummyFun()
it.dummyVar = "10";
}
}

I would like to go extreme fundamental with no fancy words. it keyword When you have one parameter you can call using it
keyword and it works so good with HOF, for instance,
private fun itKeyword(itKeyword:(String) -> Unit) {
itKeyword("")
}
fun callItFun() {
itKeyword {//it:String // this is high light over here like this
}
But if you try to do something like this:
private fun itKeyword(itKeyword:(String, Int) -> Unit) {
itKeyword("", 1)
}
fun callItFun() {
itKeyword {yourName, age -> //yourName, age is define by user
}
}
see? there is no it keyword define over here by compiler but instead we have to pass two parameters when we defined HOF but if we keep this empty, compiler will give us error Hey pass something dude, i don't what variable you are talking about there are two variables passed over here.
That means, when you have only one parameter you can call it by using it keyword.
this keyword
there are two scope or variables/properties global and local scope, when you have defined some variable as a global scope and you wanna call it in multiple methods/functions and at some place you have to use same type of another variable with the same name at local scope, in that case we will be using this keyword and why is that so?
private lateinit var mContext: Context
fun thisKeyword(mContext: Context) {
this.mContext = mContext
}
but what if we don't use this keyword and keep it like this:
private lateinit var mContext: Context
fun thisKeyword(mContext: Context) {
mContext = mContext
}
Compiler would say, mah man what have you done, didn't ring a bell in my head JK, Compiler would say, Val cannot be reassigned, wait what? we didn't init it as a val but if we see at global scope we have init it as var but guess what? compile is right. In Kotlin when pass something into functions parameter they act as a Val by default which means without this compiler was using local scope variables not global scope but when we use this keyword we tell compiler, with keyword this property over here before = is global scope this.mContext and after = is local one so this is the reason we use this keyword to refrains from variable conflict. i hope this helps, thankYou().

If you take a look at kotlin scope functions doc:
https://kotlinlang.org/docs/scope-functions.html
Some of them have "this" in their scopes and others use "it".
The difference is the following:
this: When you use scope functions that use "this", f.i. apply {} it changes the context of function's scope to the context of an object on which this function was called, e.g.:
//Outer context in outer scope
val alice = Person("Alice").apply {
//Person object's context in function's inner scope
this.age = 20 //"this" refers to the object's context
city = "London" //you can skip writing "this" because you are in the context of object
}
it: When you use functions that use "it", f.i. also {} it DOES NOT change the context of function's scope, so it remains the same as the context where the function was called, e.g.:
//Outer context in outer scope
val alice = Person("Alice").also {
//Same outer context in function's inner scope
it.age = 20 //"it" refers to the object's reference holder e.g. alice
it.city = "London" //you can not skip writing "it" because you are in outer context
}
NOTE:
The context and the scope are 2 different things.
In both cases the scope is changed so that if you declare a local variable within that scope it will not be visible from outer scope, and in 1 case the context changes and in the other one it remains the same and hence the usage of "this" and "it".

Related

kotlin add method, having difficulty understanding the "it" argument

private fun defaultOptions() {
val options = ArrayList<TextView>()
tvoptionone?.let { options.add(0, it) }
}
I am currently using the add(index, element) method in kotlin, However, I don't seem to understand what it represents in the element parameter of the add method.
These are the parameters for the add method I am trying to use
add(index, element)
it is the context object on which you've used the let function.
As you've used it with safe call operator (?.) it would only call let if object is non null.
Using ?.let ensures the lambda to be executed only when the object is non null. ?. ensures that object has to be non null and let makes that object available as it inside the lamda.
Here
tvoptionone?.let { options.add(0, it) }
it is a TextView as tvoptionone is a TextView, and it has a value same as tvoptionone.
In the below code
tvoptionone?.let { options.add(0, it) } }
it refers to tvoptionone
notice that lambda passed to let will be called only when tvoptionone is not null, so here it refers to tvoptionone and its value is not null
Questionmark after variable tvoptionone indicates that this variable can be null. If you write just:
options.add(0, tvoptionone)
and variable happens to be null then the add method will throw an error with wording like param element cannot be null or so.
Keyword let, in this particular example, is kind of a guardian against passing null into add method. If tvoptionone has some value (is not null) then it will be tvoptionone itself. Otherwise add method will not be called at all and compilation error will be avoided.
Scope functions are the ones you run on a value, and provide a function to run using that value. There's two kinds of scope functions in Kotlin - the ones where the value is passed in as a parameter (let, also etc.) and the ones where it becomes this (run, apply etc). They work the same, it just changes how you interact with the value - sometimes one is more convenient or suitable than the other
it is just the default name for the parameter passed in:
// these two are the same thing
name.let { println("Hi $it") }
name.let { it -> println("Hi $it") }
// rename it to something that reads better if you like
personData.firstName.let { name -> println("Hi $name") }
People have mentioned the null-check feature, where you can make the let block only run if the value is non-null:
name?.let { println("Hi $it, looking very not-null today") }
but another use for it is creating a temporary variable. If you have a var, it's possible the value will change while you're using it, so it's common to take a copy of it (so you know your copy won't change):
var importantNumber = 123
if (importantNumber > 100) {
// but it might have just been changed by another thread / coroutine and be < 100!
doThing(importantNumber)
}
var importantNumber = 123
val copy = importantNumber
if (copy > 100) {
// the copy can't change, so we know once we've checked it, it's fine
doThing(copy)
}
since let creates a variable to pass in as a parameter, it's basically making a copy in the same way:
var importantNumber = 123
importantNumber.let { if (it > 100) doThing(it) } // safe because 'it' won't change
and that's super important for nullable vars - if you check they're null, you need to know they'll stay null. Using ?.let guarantees that the value passed in as it will be non-null, because it's a copy that's been null-checked

How to create interchangeable class types for android fake testing?

I'm trying to create a fake class for my repository to test a view model.
As far as I understood, the key element here is to create two classes with a common interface so both classes would contain the same methods.
The problem is I get a Type mismatch when trying to initialize an object.
I tried to do the same in a simplified manner:
class fakeClass1 : fakeInterface {
override fun getAllData(): String {
return ""
}}}
class fakeClass2 : fakeInterface {
override fun getAllData(): String {
return ""
}}
interface fakeInterface {
fun getAllData(): String}
val fakeClass: fakeClass1 = fakeClass2()
But that didn't work either.
What am I missing?
Ok, I figured it out.
I was wrong to think that those two classes should be interchangeable.
I solved it by making the ViewModel take the common interface in its constructor instead of the actual repository class. This allows the ViewModel to take any class which implement this interface as it's repository.
I think you worked it out, but just so you're clear (this is an important, fundamental thing!)
val fakeClass: fakeClass1 = fakeClass2()
This is defining a variable called fakeClass that refers to an object with the fakeClass1 type. Then you assign an object with the fakeClass2 type.
But a fakeClass2 is not a fakeClass1, neither is a superclass of the other, so you can't treat one as the other. Your example is simple, but imagine you added coolFunction() to fakeClass1 - they'd now happen to have different structures, and trying to call that method on an object that doesn't have it would cause a crash.
The only thing those classes have in common, is that they both have the fakeInterface type - they are fakeInterfaces, and that guarantees they implement the stuff in that interface (your getAllData function in this case). So if you treat them both as that type instead:
val fakeClass: fakeInterface = fakeClass2()
you can use either one, because they're both fakeInterfaces (similar to how Ints and Doubles are different but they're both Numbers). Because fakeClass is now a fakeInterface, you can only access the functions and properties that a fakeInterface has - you can't call coolFunction() even if you happened to pass in a fakeClass1, because fakeInterface doesn't have that.
(You could cast the variable to fakeClass1, basically saying "oh by the way this object is actually this type as well", but at that point the type system can't guarantee you're correct unless you're explicitly checking fakeClass is fakeClass1, and it'll warn you if that's the case)
The Java tutorials are pretty good and they'll give you an overview about how the types each form a kind of "contract" you work with

How do I override a var in a Kotlin class correctly?

I am trying to overwrite a value in a class. I have the following code:
open class Balloon() {
open var textSize: Float = 20f
init {
Log.i("textSize", textSize.toString())
}
}
class BigBalloon(): Balloon() {
override var textSize = 30f
}
However, the Log prints out these values:
First Log is from Balloon(), second one is from BigBalloon(). How can it print 0.0 when I overwrote it as 30? Did I implement all of this incorrectly?
Accessing abstract methods (in this case getTextSize) in constructor is generally discouraged since it might lead to artifacts like yours.
BigBaloon property override actually does two things:
creates new internal field - BigBaloon.textSize
overrides textSize getter and setter to access that field
It's a bit counter intuitive but it does NOT modify value of Baloon.textSize field, it's left untouched and inaccessible since getter/setter no longer use it.
Your issue is when BigBaloons parent Baloon is being initialized it accesses BigBaloon.textSize which is NOT initialized at this point so it returns zero.
Kotlin initialization order is not how you understand it, the init block of the Base (Balloon) class is called before the override is done. It is explained better in this answer. And here's the initialization order given in Kotlin docs.
Put the property into primary constructor as:
Balloon(var textSize: Float = 20f) {
// ...
}
And when you want to change it, just delegate it to the constructor:
class BigBalloon: Balloon(30f)

How to cast view elements in kotlin? [duplicate]

I'm trying to build android application using Kotlin for the first time.
I want to declare on some buttons outside the OnCreate method and i can initialize them only Inside this function with findViewById.
Can i declare in simple and clean code like in java?
private Button btnProceed;
Because when converting it to Kotlin it look like:
private var btnProceed: Button? = null
And then when initialize OnClick function need to add ! sign:
btnProceed!!.setOnClickListener
What is the right and cleanest way?
This is a good use case for lateinit. Marking a property lateinit allows you to make it non nullable, but not assign it a value at the time that your Activity's constructor is called. It's there precisely for classes like Activities, when initialization happens in a separate initializer method, later than the constructor being run (in this case, onCreate).
private lateinit var btnProceed: Button
If the property is read before a real value is assigned to it, it will throw an exception at runtime - by using lateinit, you're taking the responsibility for initializing it before you access it for the first time.
Otherwise, if you want the compiler to guarantee safe access for you, you can make the Button nullable as the converter does by default. Instead of the unsafe !! operator though, which the converter often uses, you should use the safe call operator where you access the property:
btnProceed?.setOnClickListener { ... }
This will make a regular call if btnProceed is a non-null value, and do nothing otherwise.
On a final note, you can check out Kotlin Android Extensions, which eliminates the need to create properties for your Views altogether, if it works for your project.
Last edit (for now): you should also look at using lazy as described in the other answers. Being lazy is cool.
Instead of using lateinit, you can also do lazy initialization:
private val button by lazy {
findViewById(R.id.button) as Button
}
The first time you access the button property, it will execute the block once and use the result for future calls. In onCreate for example, you can now directly access it:
fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(bundle)
setContentView(R.layout.my_view)
button.setOnClickListener { ... }
}
You can do it with lateinit as #zsmb13 suggest BUT this has the disadvantage that your views will be variable instead of final. If you want them to be final you can use the lazy property delegation
By using lazy you can declare how the value will be initialized when you first try to access it so by declaring
private val btnProceed: Button by lazy {
findViewById(R.id.yourID)
}
Whenever you access your btnProceed you will have your activity (this example assume you're using an activity) loaded so you can use that method

Lifecycle methods in statically typed languages

In the last year I've become a mobile developer and a functional programming admirer.
In each of the mobile arenas there are components with lifecycle methods that make up the meat of the app. The following will use Android and Kotlin as examples, but the same applies to iOS and Swift.
In Android, there are Activity's with lifecycle methods like onCreate(). You might also define a function, onButtonClicked(), which will do exactly what the name describes.
For the purposes of the question, let's say there's a variable defined in onCreate() that is used in a button click handler onButtonClickedPrintMessageLength() (This is usually the case - onCreate() is essentially Activity's setup method).
The example class would look like this:
class ExampleActivity: Activity() {
var savedStateMessage: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
savedStateMessage = "Hello World!"
}
fun onButtonClickedPrintMessageLength() {
System.out.println(savedStateMessage?.length)
}
}
Notice the declaration of savedStateMessage as a String? (nullable string) and the use of ?. (null safe call). These are required because the compiler cant guarantee that onCreate() will be called before onButtonClickedPrintMessageLength(). As developers though, we know that onCreate will always be called first* **.
My question is how can I tell the compiler about the guaranteed order of these methods and eliminate the null checking behavior?
* I suppose it's possible to new up our ExampleActivity and call onButtonClickedPrintMessageLength() directly, thus sidestepping the Android framework and lifecycle methods, but the compiler/JVM would likely run into an error before anything interesting happened.
** The guarantee that onCreate is called first is provided by the Android framework, which is an external source of truth and might break/function differently in the future. Seeing that all Android apps are based on this source of truth though, I believe it's safe to trust.
Although this won't answer your actual question, in Kotlin you can use lateinit to tell the compiler that you'll initialize a var at a later point in time:
lateinit var savedStateMessage: String
You'll get a very specific UninitializedPropertyAccessException if you try to use this variable before initializing it. This feature is useful in use cases like JUnit, where you'd usually initialize variables in #Before-annotated method, and Android Activitys, where you don't have access to the constructor and initialize stuff in onCreate().
As mentioned in another answer, lateinit is available as an option to defer initialization to a later point in a guaranteed lifecycle. An alternative is to use a delegate:
var savedStateMessage: String by Delegates.notNull()
Which is equivalent, in that it will report an error if you access the variable before initializing it.
In Swift this is where you would use an implicitly-unwrapped Optional:
class Example: CustomStringConvertible {
var savedStateMessage: String! // implicitly-unwrapped Optional<String>
var description: String { return savedStateMessage }
init() {
savedStateMessage = "Hello World!"
}
}
print(Example()) // => "Hello World!\n"
By using the operator ! at the end of String in the second line of the example you are promising that the variable will be set before it can be used. This is accomplished in the init method of the example. It's still an Optional but code can treat it as a String since it will be automatically unwrapped before each use. You must take care that the variable is never set to nil when it might be accessed or a runtime exception may be generated.

Categories

Resources