suspend fun safeApiCall(
apiCall: () -> Call<WeatherData>
): Resource<WeatherData> {
apiCall.enqueue() //error Unresolved reference: enqueue
}
In the above code what it the meaning of () -> Call<WeatherData> and how it is different from Call<WeatherData>
Yeah the syntax is explained in the Function types section of #ADM 's link.
Basically apiCall's type is a function with no parameters (the (), like calling someFunction()) and it returns (->) a result with the type Call<WeatherData>. It's the equivalent of this:
fun apiCall(): Call<WeatherData>
except you can pass in any function that matches that signature (same parameter types, same return type). Which means you can pass in lambdas too
safeApiCall({
doStuff()
doMoreStuff()
doThingThatReturnsAWeatherDataCall()
})
(when the lambda is the only parameter it can be moved out of the parentheses, I just wanted to make it clear you're passing it in as a parameter)
If you do have a function declared somewhere that matches the signature, you can pass a reference to that in instead
fun coolWeatherFunction(): Call<WeatherData>
safeApiCall(this::coolWeatherFunction)
(you can drop the this, just showing how you refer to a function on a particular instance or class)
It can be more readable sometimes
"1 2 3 4 5".split(' ').map(String::toDouble).forEach(::println)
1.0
2.0
3.0
4.0
5.0
Related
I'm trying to gradually convert an android code base from RxJava2 to Kotlin coroutines. We are using UseCases and Repositories. I've converted one of the repository methods returning Observable to be a suspend function.
Now, there was a UseCase using Observables.combineLatest to combine 2 repository Observables, one of which is the one I converted to be suspend.
In order to still use that UseCase function as is, I converted the suspend function to be an observable using kotlinx-coroutines-rx2 that provides interop between rxjava and coroutines. I'm using this method specifically.
This is how the code looks:
override fun execute(): Observable<GetFollowersResult> {
return Observables.combineLatest(
// This suspend function is not getting called
rxObservable<ProfilesPageDomainModel>(Dispatchers.IO) { profileRepository.getFollowers() },
profileRepository.getProfile().toObservable()
) { followers, profile ->
// mapping code
}.subscribeOn(threadExecutor)
.map<GetFollowersResult> { page ->
// result
}
.onErrorReturn { throwable ->
// error
}
.observeOn(postExecutionThread)
.startWith(GetFollowersResult.InFlight)
}
But even when the observable returned by combineLatest is subscribed, the suspend function inside rxObservable doesn't get called.
Am i missing something? I cannot convert the other method to be suspend since that method is used in quite a many places and i still want to keep the suspend function since we need to use it in newer UseCases.
I suggest you replace:
rxObservable<ProfilesPageDomainModel>(Dispatchers.IO) { profileRepository.getFollowers() }
with:
rxSingle(Dispatchers.IO) { profileRepository.getFollowers() }.toObservable()
as the Rx equivalent of a suspend function is either Single (when it returns a value) or Completable (when it returns Unit).
If you check the Javadoc for rxSingle and rxObservable you will see a subtle difference:
The block function in rxObservable returns Unit, meaning inside you need to use send to emit items.
The block function in rxSingle returns T, meaning it returns the value of the suspend function called inside.
run signature looks like:
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
The way I see it, the callback (block), is being called via a receiver but without any parameters. So how someone (with a lot of credits here) , said me that:
1.
thing.run(::println)
is equivalent to :
2.
thing.run { println(this) }
I can't understant how the first is running at all... since the way i see it the println won't get any parameter
When you pass a function to a higher-order function by using the function reference syntax instead of a lambda, it doesn't matter if there's a receiver or not. The receiver is like any other parameter, and can be thought of as the first parameter.
So the syntax of run's function parameter:
T.() -> R
will be treated exactly the same as the syntax of let's function parameter:
(T) -> R
so ::println can match either of these.
This works in the other direction, too. When you pass an extension function or member function using function reference syntax, the receiver of the extension function is treated as the first parameter. Either of these is valid:
val list = listOf(1, 2, 3)
list.run(List<Int>::sum)
list.let(List<Int>::sum)
So whether there is a receiver or not only affects lambdas. The actual function signature is the same. You can see this issue if you try to define two functions like this. There will be a compiler error for the two functions having the same signature:
class Foo
fun bar(foo: Foo) {
println("Hello")
}
fun Foo.bar() {
println("Hello")
}
I've seen people using ViewModelProvider[Someclass::class.java] instead of ViewModelProvider.get(Someclass::class.java), and it compiles in Android Studio. The problem is that I couldn't find any documentation of such usage online.
With kotlin you can add operator modifiers to your function. So if you have some class with a get function and you might want to access it with [], like an array or map, you could add operator modifier.
Square brackets are translated to calls to get and set with appropriate numbers of arguments.
So this only works for functions with name get or set!
class Provider {
operator fun get(key: String)
operator fun set(key: String, value: String) { ... }
}
Then you can call the function like:
Provider().get("key") // IDE hint: should be replaced with indexing operator
Provider()["key"] // calls get()
Provider().set("key", "value") // IDE hint: should be replaced with indexing operator
Provider()["key"] = "value" // calls set()
Reference
See Kotlin Operator overloading
Kotlin allows operator overloading by marking a function as an operator function. The square brackets notation is one of these operators (indexed access operator).
Kotlin automatically interprets Java functions as operator functions if their name and signature match the requirements of a Kotlin operator function. In this case, it interprets functions named get as an "indexed access operator" if they return something, which allows you to use square bracket notation.
ViewModelProvider[Someclass::class.java] is a shorter version of ViewModelProvider.get(Someclass::class.java) there is no differences.
Today while programming I found some odd behaviour in Kotlin. I could easily go around it, but I wonder if there is some reason to it or if it is a bug in Kotlin.
I have the following interface of a delegate which delegates the showing of a dialog to the Activity.
interface ViewModelDelegate {
fun showWarningDialog(textResource: Int)
}
I want to implement it as following in the Activity. Since I know I can only do it with a context and the Activity.getContext() may return null, I wrap the code in context?.let
override fun showWarningDialog(textResource: Int) = context?.let {
//show dialog
}
However this gives me a compile error:
Return type of 'showWarningDialog' is not a subtype of the return type of the overridden member 'public abstract fun showWarningDialog(textResource: Int): Unit defined in com.some.class.path'
Which really confused me, because I don't want to return anything. So since let returns whatever the function inside returns, I was wondering if I could fix it by writing a version of let which does not return anything.
fun <T, R> T.myLet(block: (T) -> R) {
let(block)
}
However this did not remove the compiler error. I found then that the mouseover text over the error gives more information (would be nice if the compiler did). It says:
Return type is 'Unit?', which is not a subtype of overridden
Now that tells me more about the problem. Because the function context?let call may not happen, it could return null. Now there are multiple ways to go around this. I could add ?: Unit too the end of the function call or I could define showWarningDialog to return Unit? which will allow me to call it just fine in most cases. However none of these solutions are desireable. I will probably just make a normal method and call the let inside of that instead of delegating the call to it. Costs me another level of indentation and an extra vertical line:
override fun showWarningDialog(textResource: Int) {
context?.let {
//show dialog
}
}
My question is, is this behaviour intended? Why or when would this be useful that a function that returns Unit cannot be delegated to an optional function call. I am very confused by this behaviour.
Single expression function
fun foo() = <expression>
by language design is equivalent to
fun foo(): <ReturnType> {
return <expression>
}
And because Unit? is not a not a subtype of Unit, you can't return it in from a function, which returns Unit. In this sense Unit just another type in the type system, it's not something magical. So it works just as it's supposed to work with any other type.
Why or when would this be useful that a function that returns Unit cannot be delegated to an optional function call.
So basically the question is why language designers did not created a special handling to accept Unit? from a function declaring Unit as a return type. I can think about a few reasons:
It requires to create this special handling in the compiler. Special cases lead to bugs, break slim language design and complicate documentation.
As it had to be a special case, it would be not really clear and predictable for programmers. Currently it works in the same way for all types, no special treatments. It makes the language predictable, you don't need to check the documentation for every type to see if it's treated specially.
It also adds some additional safety, so to make you notice that your expression can actually skip the calculation.
So trying to summarize, I would say making this case work does not add much of value but can potentially bring some issues. That's probably why they did not add it to the language.
lets discuss this case when you have return type for example String
interface someInterface{
fun somFun():String
}
class someClass : someInterface {
var someString:String? = null
override fun somFun()=someString?.let {
//not working
it
}
override fun somFun()=someString?.let {
//working
it
}?:""
}
so what we see that when parents return type is String you cannot return Strin? it is jus kotlins nullSafety ,
what is different when you don't have return type ? lets change the code above a little
interface someInterface{
fun somFun():String
fun unitFun()
}
class someClass : someInterface {
var someString:String? = null
override fun unitFun() {
//if it is possible to return null in here
}
override fun somFun()=someString?.let {
val someresult = unitFun().toString() //you will get crash
it
}?:""
}
now we have another function without return type (unitFun Unit)
so if you can return Unit? in your subclass it will cause a crash when you want to use the result of method because it is defined asUnit and you dont need any null checks.
generally it means Unit is also type and you need to keep it null safe .
Here is a simplified example. This syntax works :
Handler().post({Log.v(TAG, "test")})
However, if I define this function
private fun doWithRunnable(toRun:Runnable) {
// whatever
}
And call it like this
doWithRunnable({Log.v(TAG, "test")})
Then I get the following error:
Required: RunnableFound: () -> Int
Both signatures look quite the same to me... what's the big difference?
In your Java example, you're taking advantage of the Java-interop feature for SAM Conversions. Unfortunately, this does not currently work for Kotlin interfaces, as the designers have deemed it unnecessary (there is some discussion on this see here).
Instead, you'd want to do something like:
fun doWithRunnable(runnable: () -> Any) {
// call it as `runnable()`
}
You could also define a typealias for this if you wanted (which is as close Kotlin comes to SAM syntax):
typealias RunMe = () -> Any
fun doWithRunnable(runnable: RunMe) {
runnable()
}
Note that currently, typealiases have to be declared at the top level, they can't be declared inside a class or method.