How to create an extension function with multiple receivers in Kotlin? - android

I want my extension function to have a couple of receivers. For example, I want function handle to be able to call methods of both CoroutineScope and Iterable instances:
fun handle() {
// I want to call CoroutineScope.launch() and Iterable.map() functions here
map {
launch { /* ... */ }
}
}
I thought this might work:
fun <T> (Iterable<T>, CoroutineScope).handle() {}
But it gives me an error:
Function declaration must have a name
I know that I can create the function with parameters, but
Is it possible to have multiple receivers for a single function and how to do that without parameters?

In the Kotlin version 1.6.20 there is a new feature called Context receivers. This is a first prototype of context receivers. This feature allows to make functions, properties and classes context-dependent by adding context receivers to their declaration. There is a new syntax for that. In front of the function declaration we can specify a list of contextual types that would be required to invoke this function. A contextual declaration does the following:
It requires all declared context receivers to be present in a caller's scope as implicit receivers.
It brings declared context receivers into the body scope of implicit receivers.
The solution with context receivers looks like the following:
context(CoroutineScope)
fun <T> Iterable<T>.handle() {
map {
launch { /* ... */ }
}
}
someCoroutineScope.launch {
val students = listOf(...)
students.handle()
}
In the context(CoroutineScope) we can declare multiple types, e.g context(CoroutineScope, LogInterface).
Since context receivers feature is a prototype, to enable it add -Xcontext-receivers compiler option in the app's build.gradle file:
apply plugin: 'kotlin-android'
android {
//...
kotlinOptions {
jvmTarget = "11"
freeCompilerArgs += [
"-Xcontext-receivers"
]
}
}

As far as I know, this is currently impossible for types that we don't control. There are plans to add such feature, it is processed under KEEP-259.
I don't know what is the planned roadmap or when we could expect it to be added, but I hope we will see at least some previews this year.

This is a very narrow case, but if your use case is that you have a higher order function where you want code in the lambda to have multiple receivers, and if the types you're wanting to combine are interfaces, you can create a class that wraps the interfaces as delegates. Within the lambda passed to the below function, you can call both Iterable and CoroutineScope functions.
class CoroutineScopeAndIterable<T>(
private val coroutineScope: CoroutineScope,
private val iterable: Iterable<T>
): CoroutineScope by coroutineScope, Iterable<T> by iterable
suspend fun <T> CoroutineScope.runSomething(
iterable: Iterable<T>,
block: suspend CoroutineScopeAndIterable<T>.() -> Unit
) {
CoroutineScopeAndIterable(this, iterable).block()
}

Here is workaround you can use:
val <T> Iterable<T>.handle: CoroutineScope.() -> Unit get() = {
map {
launch { }
}
}

Related

There are multiple DataStores active for the same file in HiltAndroidTest

I just added DataStore to our codebase. After that, I found that all sequential UI tests are failing - the first one in a test case pass but next fails with There are multiple DataStores active for the same file.
I provide a data store instance using Hilt
#InstallIn(SingletonComponent::class)
#Module
internal object DataStoreModule {
#Singleton
#Provides
internal fun provideConfigurationDataStore(
#ApplicationContext context: Context,
configurationLocalSerializer: ClientConfigurationLocalSerializer
): DataStore<ClientConfigurationLocal> = DataStoreFactory.create(
serializer = configurationLocalSerializer,
produceFile = { context.dataStoreFile("configuration.pb") }
)
}
I guess this is happening because In a Hilt test, the singleton component’s lifetime is scoped to the lifetime of a test case rather than the lifetime of the Application.
Any ideas on how to workaround this?
I had the same issue.
One solution I tried but which didn't work (correctly) is to make sure the tests, once done, remove the dataStore files (the whole folder) and close the scope (the overridden scope that you manage in a "manager" class), like so:
https://github.com/wwt/testing-android-datastore/blob/main/app/src/androidTest/java/com/wwt/sharedprefs/DataStoreTest.kt
I had this in a finished() block of a TestWatcher used for these UI tests. For some reason, this was not enough so I ended up not looking deeper into why.
Instead I just used a simpler solution: the UI tests would use their own Dagger component, which has its own StorageModule module, which provides its own IStorage implementation, which for UI tests is backed just by an in-memory map, whereas on a production Dagger module would back it up via a DataStore:
interface IStorage {
suspend fun retrieve(key: String): String?
suspend fun store(key: String, data: String)
suspend fun remove(key: String)
suspend fun clear()
I prefer this approach in my case as I don't need to test the actual disk-persistance of this storage in UI tests, but if I had needed it, I'd investigate further into how to reliably ensure the datastore folder and scope are cleaned up before/after each UI test.
I was having the same issues and I came out with a workaround. I append a random number to the file name of the preferences for each test case and I just delete the whole datastore file afterward.
HiltTestModule
#Module
#TestInstallIn(
components = [SingletonComponent::class],
replaces = [LocalModule::class, RemoteModule::class]
)
object TestAppModule {
#Singleton
#Provides
fun provideFakePreferences(
#ApplicationContext context: Context,
scope: CoroutineScope
): DataStore<Preferences> {
val random = Random.nextInt() // generating here
return PreferenceDataStoreFactory
.create(
scope = scope,
produceFile = {
// creating a new file for every test case and finally
// deleting them all
context.preferencesDataStoreFile("test_pref_file-$random")
}
)
}
}
#After function
#After
fun teardown() {
File(context.filesDir, "datastore").deleteRecursively()
}
I'd suggest for more control + better unit-test properties (ie. no IO, fast, isolated) oblakr24's answer is a good clean way to do this; abstract away the thing that you don't own that has behavior undesirable in tests.
However, there's also the possibility these tests are more like end-to-end / feature tests, so you want them to be as "real" as possible, fewer test doubles, maybe just faking a back-end but otherwise testing your whole app integrated. If so, you ought to use the provided property delegate that helps to ensure a singleton, and declare it top-level, outside a class, as per the docs. That way the property delegate will only get created once within the class-loader, and if you reference it from somewhere else (eg. in your DI graph) that will get torn down and recreated for each test, it won't matter; the property delegate will ensure the same instance is used.
A more general solution, not limited to Hilt, would be to mock Context.dataStoreFile() function with mockk to return a random file name.
I like this approach as it doesn't require any changes on the production code.
Example of TestWatcher:
class CleanDataStoreTestRule : TestWatcher() {
override fun starting(description: Description) {
replaceDataStoreNamesWithRandomUuids()
super.starting(description)
}
override fun finished(description: Description) {
super.finished(description)
removeDataStoreFiles()
}
private fun replaceDataStoreNamesWithRandomUuids() {
mockkStatic("androidx.datastore.DataStoreFile")
val contextSlot = slot<Context>()
every {
capture(contextSlot).dataStoreFile(any())
} answers {
File(
contextSlot.captured.filesDir,
"datastore/${UUID.randomUUID()}",
)
}
}
private fun removeDataStoreFiles() {
InstrumentationRegistry.getInstrumentation().targetContext.run {
File(filesDir, "datastore").deleteRecursively()
}
}
}
and then use it in tests:
class SomeTest {
#get:Rule
val cleanDataStoreTestRule = CleanDataStoreTestRule()
...
}
The solution assumes that you use Context.dataStoreFile() and that the file name does not matter. IMO the assumptions are reasonable in most cases.

What is the purpose of kotlin contract

Was reading the apply function code source and found
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
and contract has an empty body, experimental
#ContractsDsl
#ExperimentalContracts
#InlineOnly
#SinceKotlin("1.3")
#Suppress("UNUSED_PARAMETER")
public inline fun contract(builder: ContractBuilder.() -> Unit) { }
what is the real purpose of contract and is it here to stay in the next versions?
What is the real purpose of contract
The real purpose of Kotlin contracts is to help the compiler to make some assumptions which can't be made by itself. Sometimes the developer knows more than the compiler about the usage of a certain feature and that particular usage can be taught to the compiler.
I'll make an example with callsInPlace since you mentioned it.
Imagine to have the following function:
fun executeOnce(block: () -> Unit) {
block()
}
And invoke it in this way:
fun caller() {
val value: String
executeOnce {
// It doesn't compile since the compiler doesn't know that the lambda
// will be executed once and the reassignment of a val is forbidden.
value = "dummy-string"
}
}
Here Kotlin contracts come in help. You can use callsInPlace to teach the compiler about how many times that lambda will be invoked.
#OptIn(ExperimentalContracts::class)
fun executeOnce(block: ()-> Unit) {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
}
#OptIn(ExperimentalContracts::class)
fun caller() {
val value: String
executeOnce {
// Compiles since the val will be assigned once.
value = "dummy-string"
}
}
is it here to stay in the next versions?
Who knows. They are still experimental after one year, which is normal for a major feature. You can't be 100% sure they will be out of experimental, but since they are useful and they are here since one year, in my opinion, likely they'll go out of experimental.

Kotlin about passing lambda as property

I am confusing about merit of writing code as following 2 case:
class TestA {
private val foo: Boolean by lazy {
// Here is logic that return true or false
}
Case 1:
fun main() {
TestB({foo})
}
Case 2:
fun main() {
TestB(foo)
}
}
Case 1:
class TestB(private val isFoo: () -> Boolean ) {
fun checkFoo(): Boolean {
return isFoo.invoke()
}
}
Case 2:
class TestB(private val isFoo: Boolean ) {
fun checkFoo(): Boolean {
return isFoo
}
}
When should I use case 1 or case 2 ?
By the way, please let me know how does invoke() method work?
You only pass lambdas into other class constructors if you want something to be invoked on the other end, that might make sense if used as a callback, or if you need to have a function that creates objects again and again rather than being static. In this case, you'd store the lambda for later referral and invoke it whenever needed. If you just pass a static instance around, that is for example foo in your code, there's no reason for a lambda. You should always prefer not to use lambdas for constructors; scenarios in which they are useful or necessary are rather rare IMO.
As to your question regarding invoke: Kotlin has a number of functions that work "by convention", e.g. rangeTo, equals, contains, compareTo, the index operators and also invoke. Learn about conventions here.
Now, whenever a class provides the invoke operator, you can call instances of that class as if they were functions:
class InvokeMe(){
operator fun invoke(value: Int) = println("invoked with $value")
}
val obj = InvokeMe()
//both are compiled to the same code
obj(10)
obj.invoke(5)
Since every lambda is being compiled into a Function instance (see kotlin.jvm.functions) which comes with an implementation of invoke, you can call lambdas as shown above, i.e., using lambda(args) or lambda.invoke(args)
.invoke() will simply call your lambda and give your result, same as calling a function.
As for when you should pass a lambda or an actual value, it very depends.
Personally, I would only suggest using lambdas in very specific situations, overusing them can make your code very confusing and hard to refactor. If you just want a result to be passed into the function, just pass the actual value. Don't make someone else call .invoke().
But a few good example for a lambda are callsbacks, or onClickListeners.
// A login network request with a lambda handling the result.
fun login( username: String, password: String, onResult: (Result) -> Unit ) {
// do some network call, and return a Result.
}
// note: if the last param is a lambda, you can simply move it outside the function call like this.
login( username, password ) { result ->
// use the result of the network request.
}
Hopefully that helps.

How to declare an interface that works with lambda? [duplicate]

This question already has answers here:
Lambda implementation of interface in kotlin
(5 answers)
Closed 4 years ago.
I declared a simple interface like this:
interface OnSomethingReadyListener {
fun onSomethingReady()
}
And of course a setter:
private val onSomethingReadyListeners = ArrayList<OnSomethingReadyListener>()
fun addOnSomethingReadyListener(callback: OnSomethingReadyListener) {
onSomethingReadyListeners.add(callback)
}
But then I found that I cannot use lambda:
something.addOnShopDataReadyListener { progressbar.visibility = View.GONE }
IDE gave me an error:
Type mismatch.
Required: SomeClass.OnSomethingReadyListener
Found: () -> Unit
Suppose I want to stick to using lambda instead of anonymous class (object : OnSomethingReadyListener {...}). How should I declare OnSomethingReadyListener?
SAM (single abstract method) conversion is only support for Java interfaces, not Kotlin interfaces.
The documentation states this reasoning:
Also note that this feature works only for Java interop; since Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary and therefore unsupported.
KT-7770 requests this functionality, if you want to track whether it is being considered or not.
The recommended way to achieve what you want is to replace the OnSomethingReadyListener with a parameter of type () -> Unit like so:
private val onSomethingReadyListeners = ArrayList<() -> Unit>()
fun addOnSomethingReadyListener(callback: () -> Unit) {
onSomethingReadyListeners.add(callback)
}
You can then invoke those listeners with something like
onSomethingReadyListeners.forEach { it.invoke() }
You have to declare the function as this:
fun addOnSomethingReadyListener(block: (OnSomethingReadyListener) -> Unit)
And then you can call it as follow:
addOnSomethingReadyListener {
// TODO
}

How to pass null to an Observable with nullable type in RxJava 2 and Kotlin

I initialize my variable like this:-
val user: BehaviorSubject<User?> user = BehaviorSubject.create()
But I can't do this. IDE throws an error:-
user.onNext(null)
And doing this, IDE says u will never be null:-
user.filter( u -> u!=null)
As Guenhter explained, this is not possible. However, instead of proposing the null-object pattern, I'd recommend an implementation of the Optional type:
data class Optional<T>(val value: T?)
fun <T> T?.asOptional() = Optional(this)
This makes your intent much clearer, and you can use a destructuring declaration in your functions:
Observable.just(Optional("Test"))
.map { (text: String?) -> text?.substring(1)?.asOptional() }
.subscribe()
Using the null-object pattern here can cause more bugs than it solves.
If you use rxkotlin/rxjava 2.0 (I assume so) than the answer is: you can't. The reason is explained here.
This is a break of the interface. Have a look at the Observable Interface
public interface Observer<T> {
/** ... */
void onSubscribe(#NonNull Disposable d);
/** ... */
void onNext(#NonNull T t);
/** ... */
void onError(#NonNull Throwable e);
/** ... */
void onSubscribe(#NonNull Disposable d);
/** ... */
void onNext(#NonNull T t);
/** ... */
void onError(#NonNull Throwable e);
...
The #NonNull will be considered by the Kotlin compiler and therefore you CAN'T pass null.
Even if you could, the onNext would immediately throw an error:
#Override
public void onNext(T t) {
if (t == null) {
onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
return;
}
...
}
If you really need such a thing as null you have to fake it. e.g. by creating a static object of User which represents your null-element.
e.g.
data class User(val username, val password) {
companion object {
val NULL_USER = User("", "")
}
}
...
val user = BehaviorSubject.create<User>()
...
user.onNext(User.NULL_USER)
...
user.filter { it !== User.NULL_USER }
But if is somehow possible, try to avoid the null concept and maybe think of another solution where this isn't needed.
Thank you very much for all your answers but I ultimately went with this solution:-
class UserEnvelope(val user:User?) {}
And using this in the observables.
This best suited my requirements.
I am new to Kotlin so I don't know how to use Optionals. But from what I understand, I would have to typecast it to User type everytime I need to observe the values right?
To implement the solution mentioned in the nhaarman's answer, you can use the util class Optional (doc) from the Android SDK which was added in API level 24.
If your app's minSdkVersion less than 24 then you still need to implement it by yourself.
Since RxJava 2 does not support null values, there are some other acceptable solutions you can use:
Work with a custom or third party wrapper library of Optionals like some of the posted answers suggest. When I got rid of Java in favour of Kotlin, Optionals went away in the same package since Kotlin per se supports nullability as part of its type System. Just by this change the code was much more clearer, and I personally don't want to get Optionals back in my code as long as I can avoid them.
Emit Any class instances with your subject type. For example you could create an Empty.INSTANCE enum class which would emulate the null value and then filter by the enum class.
The last one is the one I use and prefer being a variant of the previous solution and is based on specialisations. Our friends of JetBrains always emphasise that classes are very cheap in Kotlin, so this would be a quick example to distinguish logged users and not logged ones:
abstract class SessionUser
sealed class LoggedUser(val username: String, val password: String) : SessionUser()
sealed class LogoutUser : SessionUser()
private val user = BehaviorSubject.create<SessionUser>()
private val loggedUser =
user.filter { it is LoggedUser }.cast(LoggedUser::class.java)
fun login(username: String, password: String) {
user.onNext(LoggedUser(username, password))
}
fun logout() {
user.onNext(LogoutUser())
}
I've taken an approach similar to Optional<User> and UserEnvelope. I make a simple User class and a ReifiedUser class that inherits from it. The User class has a companion object that has a NONE instance. The BehaviorSubject is instantiated with the User.NONE instance. It looks something like this:
open class User {
companion object {
val NONE = User()
}
}
class ReifiedUser(
#field:JsonProperty(J.FirstName) val firstName: String,
#field:JsonProperty(J.LastName) val lastName: String
) : User()
My BehaviorSubject is instantiated like this:
val activeUser: BehaviorSubject<User> = BehaviorSubject.createDefault(User.NONE)
And wherever I need to use activeUser I either flatMap it to Observable.empty() if it's NONE or just figure out what it is and what to do in the subscriber.
I don't like mixing java Optional with kotlin nullable because mixing map and let gets really confusing and ugly. This way it's very obvious what's going on.
I think it makes more sense to write a container class such as Result. An example of that would be
data class Result<T>(value: T?, error: Throwable?)
Usage
Observable.create { observer ->
upstreamService.listen(object: UpstreamListener {
onSuccess(data: User) {
observer.onSuccess(Result(data))
}
onError(exception: Throwable) {
observer.onSuccess(Result(null, exception))
}
}
}

Categories

Resources