Kotlin ?.let + ?:run + CompletableFuture unexpected behaviour - kotlin

Here is the code, it runs as expected, no exception
fun main() {
var mayBeEmptyString: String?
mayBeEmptyString = "1";
mayBeEmptyString?.let {
println("Inside let")
} ?: run {
throw RuntimeException("Inside run")
}
}
Output:
Inside let
And here is the code that I am not able to understand how it works:
fun main() {
var mayBeEmptyString: String?
mayBeEmptyString = "1";
mayBeEmptyString?.let {
// println("Inside let")
CompletableFuture.runAsync{ println("Inside let")}.join()
} ?: run {
throw RuntimeException("Inside run")
}
}
Output:
Inside let
Exception in thread "main" java.lang.RuntimeException: Inside run
at com.test.TestKt.main(test.kt:15)
at com.test.TestKt.main(test.kt)
Can anyone explain what is going on here? Thank you.

runAsync is meant for running a task that doesn't return a value, so you get a CompletableFuture<Void>, and attempting to read its value with get or join will give you null.
You then make this null result of join the result of your let block, which will cause your run block to be executed.

Because CompletableFuture.join() return null value cause
mayBeEmptyString?.let {
// println("Inside let")
CompletableFuture.runAsync{ println("Inside let")}.join()
}
will be null
and run { } will be executed

Related

Getting data from Datastore for injection

I am trying to retrieve the base url from my proto datastore to be used to initialize my ktor client instance I know how to get the data from the datastore but I don't know how to block execution until that value is received so the client can be initialized with the base url
So my ktor client service asks for a NetworkURLS class which has a method to return the base url
Here is my property to retrieve terminalDetails from my proto datastore
val getTerminalDetails: Flow<TerminalDetails> = cxt.terminalDetails.data
.catch { e ->
if (e is IOException) {
Log.d("Error", e.message.toString())
emit(TerminalDetails.getDefaultInstance())
} else {
throw e
}
}
Normally when I want to get the values I would do something like this
private fun getTerminalDetailsFromStore() {
try {
viewModelScope.launch(Dispatchers.IO) {
localRepository.getTerminalDetails.collect {
_terminalDetails.value = it
}
}
} catch(e: Exception) {
Log.d("AdminSettingsViewModel Error", e.message.toString()) // TODO: Handle Error Properly
}
}
but in my current case what I am looking to do is return terminalDetails.backendHost from a function and that where the issue comes in I know I need to use a coroutine scope to retrieve the value so I don't need to suspend the function but how to a prevent the function returning until the coroutine scope has finished?
I have tried using async and runBlocking but async doesn't work the way I would think it would and runBlocking hangs the entire app
fun backendURL(): String = runBlocking {
var url: String = "localhost"
val job = CoroutineScope(Dispatchers.IO).async {
repo.getTerminalDetails.collect {
it.backendHost
}
}
url
}
Can anyone give me some assistance on getting this to work?
EDIT: Here is my temporary solution, I do not intend on keeping it this way, The issue with runBlocking{} turned out to be the Flow<T> does not finish so runBlocking{} continues to block the app.
fun backendURL(): String {
val details = MutableStateFlow<TerminalDetails>(TerminalDetails.getDefaultInstance())
val job = CoroutineScope(Dispatchers.IO).launch {
repo.getTerminalDetails.collect {
details.value = it
}
}
runBlocking {
delay(250L)
}
return details.value.backendHost
}
EDIT 2: I fully fixed my issue. I created a method with the same name as my val (personal decision) which utilizes runBlocking{} and Flow<T>.first() to block while the value is retrieve. The reason I did not replace my val with the function is there are places where I need the information as well where I can utilize coroutines properly where I am not initializing components on my app
val getTerminalDetails: Flow<TerminalDetails> = cxt.terminalDetails.data
.catch { e ->
if (e is IOException) {
Log.d("Error", e.message.toString())
emit(TerminalDetails.getDefaultInstance())
} else {
throw e
}
}
fun getTerminalDetails(): TerminalDetails = runBlocking {
cxt.terminalDetails.data.first()
}

My non-nullable ArrayList is returning a null upon calling it and throwing a Null Pointer Exception

Running this on IntelliJ IDEA 2020.11 using JDK 14 and coding it in Kotlin.
I have a class with a variable menuComponents which is an ArrayList full of MenuComponents, but it's empty at initialization.
var menuComponents: ArrayList<MenuComponent> = ArrayList()
I want to edit the components so I wrote this.
for (component in menuComponents) {
//Do some stuff. The stuff doesn't matter, it throws an exception if I leave it blank
}
When I call on this method, I get a null pointer exception. Additionally, the for loop doesn't even matter.
class NPE() {
init {
refreshProperties()
}
var menuComponents: ArrayList<Double> = ArrayList()
fun refreshProperties() {
refreshJMenuComponents()
}
private fun refreshJMenuComponents() {
val i = menuComponents.size
println("$i is the length.")
for (index in 0 until menuComponents.size) {
val component = menuComponents[index]
println("Refreshed component: $component")
}
}
}
fun main() {
NPE()
}
This statement errors out too. I don't change menuComponents at all before I call these, so it should just be equal to a blank array list. I don't know why it's throwing a Null Pointer Exception.
menuComponents = arrayListOf(//component 1, component 2)
If I try running any of the previous statements on menuComponents now, it still throws a Null Pointer Exception. The value is not nullable, and I am explicitly setting it equal to something, so why is it erroring out at all? It should just not even compile if there is a null object somewhere? It compiles and then throws an exception.
Is this some sort of possible bug or am I just missing something?
I just needed to move the variable initialization above the init block.
class NPE() {
var menuComponents: ArrayList<Double> = ArrayList()
init {
refreshProperties()
}
fun refreshProperties() {
refreshJMenuComponents()
}
private fun refreshJMenuComponents() {
val i = menuComponents.size
println("$i is the length.")
for (index in 0 until menuComponents.size) {
val component = menuComponents[index]
println("Refreshed component: $component")
}
}
}
fun main() {
NPE()
}

How to do coVerifyOrder of livedata.postValue(any()) - It is returning io.mockk.MockKException: Failed matching mocking signature for

Scenario -
Hi I am new in testing using mockk. I want to test the order in which methods are being called in viewmodel. And I want to test livedata.postValue in verify block{} but mockk is giving an exception. Kindly also help me to understand the meaing of exception
MyViewModel.kt
fun doWork(showError: Boolean = false) {
launch {
val result = getImageUseCase.getImages()
if (!showError) {
withContext(uiDispatcher) {
liveDataResponse.postValue(LiveDataResult.success(Response(result)))
}
} else {
throw Exception("Unknown")
}
}
}
MyViewModelTest.kt
#Test
fun verifyOrderOfMethodExecution(){
coEvery { getImageUseCase.getImages() } returns 1
myViewModel.doWork()
coVerifyOrder {
getImageUseCase.getImages()
myViewModel.liveDataResponse.postValue(any())
}
}
Exception -
io.mockk.MockKException: Failed matching mocking signature for
SignedCall(retValue=, isRetValueMock=true, retType=class kotlin.Any, self=GetImageUseCase(usecase#1), method=getImages(Continuation), args=[Continuation at com.rahullohra.lab.MyViewModelTest$verifyOrderOfMethodExecution$2.invokeSuspend(MyViewModelTest.kt:79)], invocationStr=GetImageUseCase(usecase#1).getImages(continuation {}))
left matchers: [any()]

DisposableObserver<> is not subtype of Observer<>

I am trying to convert this RxJava/RxAndroid lesson to RxKotlin/RxAndroid.
At the method at Example5 I get error from picture
My getNotesObservable() function is:
fun getNotesObservable(): Observable<Note>{
val notes: List<Note> = prepareNotes()
return Observable.create {
for (note in notes) {
if (!it.isDisposed){ // onNext only if observable is not disposed
it.onNext(note)
}
}
if (!it.isDisposed) {
it.onComplete()
}
}
}
and part with error is:
disposable.add(
getNotesObservable().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map{
it.note = it.note.toUpperCase()
}
.subscribeWith(getNotesObserver())
)
So, what's should I change in my code?
All that I needed was return statement (from picture below)
Thanks for answer

Does kotlin coroutines have async call with timer?

Does Kotlin has possibility to call function async() in coroutines with some time, witch will return default result after time completion?
I found that it's possible to only call await, and than infinity wait the result.
async {
...
val result = computation.await()
...
}
But real production case than you need to return either default result or exception. What is proper way to do something in Kotlin coroutines? Like something similar to this:
async {
...
val timeout = 100500
val result: SomeDeferredClass = computation.await(timeout)
if (result.isTimeout()) {
// get default value
} else {
// process result
}
...
}
You can use the withTimeout-function. It will throw a CancellationException when it times out. You could catch this exception and return your default value.
Something like this:
async {
...
val timeout = 100500L
try {
withTimeout(timeout) {
computation.await()
}
...
} catch (ex: CancellationException) {
defaultValue
}
}
You could also use the withTimeoutOrNull-function, which returns null on timeout. Like this:
async {
...
val timeout = 100500L
withTimeoutOrNull(timeout) { computation.await() } ?: defaultValue
}
This approach won't let you differentiate between a timeout and a computation that returns null though. The default value would be returned in both cases.
For more info, see here: https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#timeout
Combining both Extensions and #marstran solution I came to a solution that may fit better to your requirements of having the await function with timeout and default value. Also I think it's a cleaner solution
Just define the extension function:
suspend fun <T> Deferred<T>.await(timeout : Long, defaultValue : T) =
withTimeoutOrNull(timeout) { await() } ?: defaultValue
And you can use it anywhere. Instead of
async {
...
val timeout = 100500L
withTimeoutOrNull(timeout) { computation.await() } ?: defaultValue
}
You can do simply
async {
val timeout = 100500L
computation.await(timeout, defaultValue)
}