Kotlin scoping functions: Android EditText supports *apply*, *let* and *run* but not *with* - kotlin

I have an Activity that holds a EditText. Imported via:
import kotlinx.android.synthetic.main.myActivity.*
I would like to use the with function but for some reason only the other scoping function are accessible:
apply, also, run, runCatching, takeIf and takeUnless are shown, but not with (and yes, I scrolled further down and even typed it out. Its marked as unknown if I do so).
To clarify, here what actually happens:
Are there rules to when an object has these functions and when not?

It is because apply, also, run, runCatching, takeIf and takeUnless are extensions, when with is function with 2 parameters. Here is good article about this.
You can use with like this:
with(editText) {
//your code
}
But you can not call with, as you tried to do this:
editText.with() //compilation error
Update:
with function purpose is to call object methods more easy, you don't need to write something like this:
someObject.a();
someObject.b();
someObject.c();
//etc
When you use with, you can write it like this:
with(someObject) {
a();
b();
c();
//etc
}

You're not getting any suggestions about with function because, it has no source type as extension, while other extensions contains source attached to it. How?
Look at the difference below :
with function
#kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
...
return receiver.block()
}
let function
#kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
...
return block(this)
}
Here T is the source type for the extension.

Related

Add Extension function in kotlin to all classes

Is it possible to add extension function to all classes? I was thinking about adding it to some common base class like Object. Is it possible?
With Kotlin, Any is the super type like Object for Java.
fun Any.myExtensionFunction() {
// ...
}
And if you want to support null-receiver too:
fun Any?.myExtensionFunction() {
// ...
}
It depends on whether you want to use the type of the object in the signature (either in another argument, or in the return type). If not, use Any(?) as Kevin Robatel's answer says; but if you do, you need to use generics, e.g. (from the standard library)
inline fun <T, R> T.run(block: T.() -> R): R
inline fun <T> T.takeIf(predicate: (T) -> Boolean): T?
etc.

Typealias and extension function in Kotlin

I need some help understanding the following code as I am complete Kotlin newbie. This is from a kotlin post I found online
typealias genericContext<T> = Demo<T>.() -> Unit
class Demo<T> {
infix fun doThis(block: genericContext<T>) = block()
fun say(obj: T) = println(obj.toString())
}
fun main(args: Array<String>)
{
val demo = Demo<String>()
demo doThis { say("generic alias") }
}
So I understand that because of the infix we can skip the usual method call syntax i.e. demo.doThis and do demo doThis.
But I don't understand the following:
typealias genericContext<T> = Demo<T>.() -> Unit
This seems to associate the string genericContext<T> with something that looks like a lambda but I don't get the .() part. That extends Demo with a function ()? I am confused on how this works. Could someone shed some light?
typealias genericContext<T> = Demo<T>.() -> Unit is a type alias. It is simply giving a new name to the type on the right hand side. This means that declaration of doThis in Demo is equivalent to this:
infix fun doThis(block: Demo<T>.() -> Unit) = block()
Now for the type Demo<T>.() -> Unit:
This is a function type. A function of this type takes a Demo as it's receiver argument, and returns Unit. It is therefore the type of all functions defined either in the Demo class or as an extension on the Demo class.
When you provide a lambda of this type (for example when you call the doThis function), then this will point to a Demo-object inside the lambda. For example:
someDemo.doThis {
/* "this" is an object of type `Demo`.
* In this case it's actually "someDemo", because the implementation of "doThis"
* calls "block" on the implicit "this".
*/
this.say("Hey!")
}

Kotlin Extension Function on Observable<T>.subscribe does not work

I'm trying to write an extension function for Observable.subscribe which automatically logs errors.
fun <T> Observable<T>.subscribeAndLogE(onNext: Consumer<in T>): Disposable =
subscribe(onNext, ErrorConsumer())
The ErrorConsumer works and presumably logs the error, but subscribeAndLogE does not accept lambdas like .subscribe() does.
observer.subscribe {
//works
}
observer.subscribeAndLogE {
//does not work
}
It says:
With that OnboardingScreen being whichever value T would normally be.
I don't see the original Consumer<in T> in Observable doing anything special to accept lambdas. What am I doing wrong here?
You are passing a parameter of type Consumer to the function. You need to pass a function for the lambda syntax to work. This would work the way you want to:
fun <T> Observable<T>.subscribeAndLogE(onNext: (it : T) -> Unit): Disposable =
subscribe({ onNext(it) },{ throwable -> Log(throwable) })
and use it like so:
observer.subscribeAndLogE {
//works
}

Kotlin - How to create an alias function of RxJava flatmap()?

I tried to create an alias function to Flowable.flatmap() as follow, but compile error.
fun <T, R> Flowable<T>.then(mapper: Function<T, Publisher<R>>): Flowable<R> {
return flatMap(mapper)
}
The error is : One type argument expected for interface Function<out R> defined in kotlin
Have any idea? Thanks!
The flatMap takes a java.util.function.Function, the actually error is you didn't import the java.util.function.Function in your Kotlin file, but I don't suggest you use the java-8 functions because you can't take advantage of the SAM Conversions to use the lambda directly from the Kotlin code which defined with java-8 functional interface as parameter type.
You should replace Function with Function1, since the Function interface is a Kotlin marker interface only. for example:
// v--- use the `Function1<T,R>` here
fun <T, R> Flowable<T>.then(mapper: Function1<T, Publisher<R>>): Flowable<R> {
return flatMap(mapper)
}
OR use the Kotlin function type as below, for example:
// v--- use the Kotlin function type here
fun <T, R> Flowable<T>.then(mapper: (T) -> Publisher<R>): Flowable<R> {
return flatMap(mapper)
}

How to check generic type in Kotlin?

I have class:
class Generic<T : SuperType>() { ... }
And this code is't correct, but cast s to type T:
fun typeCheck(s: SuperType) {
when(s) {
is T -> // Do something
}
}
If use: s as T - this cast will show warning (unsafe cast).
How check that s is T type?
If you need to check if something is of generic type T you need to to have an instance of Class<T> to check against. This is a common technique in Java however in Kotlin we can make use of an inlined factory method that gets us the class object.
class Generic<T : Any>(val klass: Class<T>) {
companion object {
inline operator fun <reified T : Any>invoke() = Generic(T::class.java)
}
fun checkType(t: Any) {
when {
klass.isAssignableFrom(t.javaClass) -> println("Correct type")
else -> println("Wrong type")
}
}
}
fun main(vararg args: String) {
Generic<String>().checkType("foo")
Generic<String>().checkType(1)
}
Generic types are not reified on the JVM at runtime, so there's no way to do this in Kotlin. The warning is correct because the compiler can't possibly generate any instruction that will fail when the cast is done, so the cast is unchecked, meaning that the program may or may not break at some point later instead.
A related feature which might be of use is reified type parameters in inline functions. Classes can't have reified type parameters though, so if you elaborate a bit more on your use case, I can try helping you achieve what you seem to need.
I know that I'm kinda late to this thread, but I just want to recap on the answer provided by Alexander Udalov.
It is, indeed, impossible to determine the type of a generic parameter in Kotlin unless you're using inline functions and declaring the generic type as reified.
Not sure if I'll be able to answer this question entirely and accurately, but I feel like my contribution might still be valuable for someone who is attempting to do just that. So let's say you have a few data classes, and you want to check which type you're dealing with.
You could use a function like that:
inline fun <reified T> checkType() = when (T::class) {
TypeA::class -> println("TypeA")
else -> println("Type not recognized")
}
however, functions that call it must also be inline, so you might have to write something like
inline fun <reified T> someOtherFunction(data: T) {
checkType<T>
}
however, if you cannot allow for an inline function (let's say in an interface!), you can kinda 'cheat' the system by saying, for example
class AmazingTypes {
inline fun <reified T> checkType(genericParameter: T) = when (T::class) {
TypeA::class -> println("TypeA")
else -> println("Type not recognized")
}
}
fun myAwesomeMethod(someParameter: Any) {
val amazingClass = AmazingClass()
amazingClass.checkType(someParameter)
}
This is also example.
inline fun <reified T: ApiResponse> parseJson(body: String): T {
// handle OkResponse only
val klass = T::class.java
if (klass.isAssignableFrom(OkResponse::class.java)) {
return T::class.java.newInstance()
}
// handle others
return gson.from(body, T::class.java)
}