converting java to kotlin,
java code
private boolean hasEndpoint() {
if (mSettings == null || mSettings.getEndpoint() == null) {
if (isDebugMode()) {
throw new IllegalArgumentException("endpoint is not set !!!");
}
return false;
}
return true;
}
public void doAction_1(...) {
if (!hasEndpoint()) {
callback.onError(ENDPOINT_UNINITIALIZED, "ERROR_END_POINT_NOT_SET");
return;
}
//do the action with valid endpoint
doSomething_1(mSettings.getEndpoint());
}
the kotlin:
private fun hasEndpoint(): Boolean {
if (mSettings?.endpoint == null) {
require(!isDebugMode) { "endpoint is not set !!!" }
return false
}
return true
}
fun doAction_1() {
if (!hasEndpoint()) {
callback.onError(ENDPOINT_UNINITIALIZED, "ERROR_END_POINT_NOT_SET")
return
}
//do the action with valid endpoint
doSomething_1(mSettings!!.getEndpoint());
}
There are multiple functions (i.e. doAction_1(), doAction_2() ...) doing the same check using hasEndpoint().
What is Kotlin idiomatic way to do something like this?
You can use a concept similar to Python decorators:
// Add your check here
fun withCheck(action: () -> Unit) {
if (!hasEndpoint()) {
callback.onError(ENDPOINT_UNINITIALIZED, "ERROR_END_POINT_NOT_SET")
return
}
action()
}
// Add your actions enclosed with `withCheck`
fun action1() = withCheck {
doSomething_1(mSettings!!.getEndpoint());
}
fun action2() = withCheck {
doSomething_2(mSettings!!.getEndpoint());
}
You can use a property instead of a function for hasEndpoint or rather hasNoEndpoint and use when in place of if else
private val hasNoEndpoint: Boolean
get() = when {
mSettings?.endpoint != null -> false
isDebugMode -> throw IllegalArgumentException("endpoint is not set !!!")
else -> true
}
// use this in withCheck function as in enzo's answer
fun withEndpoint(action: () -> Unit): Unit = when {
hasNoEndpoint -> callback.onError(ENDPOINT_UNINITIALIZED, "ERROR_END_POINT_NOT_SET")
else -> action()
}
Related
I have to read a value from a flow just once and then immediately return it from the function. I have a piece of code like this:
fun getValue(): String {
val flow = getFlow()
Mainscope().launch {
flow.collect {
return it
}
}
}
But this is giving me an error saying that I cant return from inside collect. I know that ideally I should be returning the flow object itself and then calling collect on the returned flow. But it is not possible, since getValue() has already been used in several places by now and I cannot change its signature now.
I have tried using suspend and synchronized as follows:
// call the function like this: runBlocking { print(getValue()) }
suspend fun getValue(): String {
val flow = getFlow()
flow.collect {
return it
}
}
and
fun getValue(): String {
val lock = Any()
var value: String? = null
val flow = getFlow()
MainScope().launch {
flow.collect {
synchronized(lock) {
value = it.toString()
lock.notify()
}
}
}
synchronized(lock) {
while (value == null) lock.wait()
return value as String
}
}
But in both cases the control never reaches inside collect. So I tried putting collect inside a new thread:
...
val flow = getFlow()
thread {
MainScope().launch {
flow.collect {
synchronized(lock) {
value = it.toString()
lock.notify()
}
}
}
}
synchronized(lock) {
...
but its still the same. So how do I read the value from the flow in a non-suspending way and return it immediately?
To get first value of the flow:
fun getFlow() = flowOf("one","two","three")
fun getValue(): String {
var r = ""
runBlocking {
r = getFlow().firstOrNull()?:"none"
}
return r
}
println(getValue())
//one
To get last value of the flow:
fun getValue(): String {
var r = ""
runBlocking {
getFlow().collect {
r = it
}
}
return r
}
println(getValue())
//three
I'd like to simplify this expression, especially "isDigit" and "isLetter" case. How to do it?
smoothInput.forEach { char ->
when {
char.isValidOperator() -> {
output.push(char)
}
char.isDigit() -> {
if (output.isNotEmpty() && output.last()!!.isNumeric()) output.addToLast(char)
else output.push(char)
}
char.isLetter() -> {
if (output.isNotEmpty() && output.last()!!.isValidVariableName()) output.addToLast(char)
else output.push(char)
}
else -> {
throw InvalidIdentifierException()
}
}
}
I think, that it isn't important, but it's much better to add code here than in comment
output is InputStack Type:
class InputStack : Stack<String> {
override val storage = mutableListOf<String>()
fun push(e: Char) = push(e.toString())
fun push(e: Operator) = push(e.toString())
fun addToLast(e: Char) {
storage[storage.size - 1] += e.toString()
}
}
Stack Interface:
interface Stack<T> {
val storage: MutableList<T>
fun asString(): String = buildString {
appendLine("----top----")
storage.asReversed().forEach {
appendLine(it)
}
appendLine("-----------")
}
fun push(element: T) = storage.add(element)
fun pop(): T {
if (storage.size == 0) throw EmptyStackException()
return storage.removeAt(storage.size - 1)
}
fun isEmpty(): Boolean = storage.isEmpty()
fun isNotEmpty(): Boolean = !isEmpty()
fun last(): T? = storage.lastOrNull()
fun forEach(action: (T) -> Unit) {
for (element in storage) action(element)
}
}
You can extract some common parts in the following way:
fun addCharToOutputConditionally(char: Char, output: InputStack, conditionOnLast: (String) -> Boolean) {
if (output.isNotEmpty() && conditionOnLast(output.last()!!)) output.addToLast(char)
else output.push(char)
}
smoothInput.forEach { char ->
when {
char.isValidOperator() -> {
output.push(char)
}
char.isDigit() -> {
addCharToOutputConditionally(char, output) {
it.isNumeric()
}
}
char.isLetter() -> {
addCharToOutputConditionally(char, output) {
it.isValidVariableName()
}
}
else -> {
throw InvalidIdentifierException()
}
}
}
However, in cases like this, I don't think it's usually worth spending the time to refactor it this way, considering that there's little to gain by doing so: the resulting code is even longer and arguably harder to read than the original one.
The new when expression:
smoothInput.forEach { char ->
when {
char.isValidOperator() -> output.push(char)
char.isDigit() -> output.addToLastConditionally(char) { it.isNumeric() }
char.isLetter() -> output.addToLastConditionally(char) { it.isValidVariableName() }
else -> throw InvalidIdentifierException()
}
}
I've change the addToLast function in InputStack for addToLastConditionally
fun addToLastConditionally(char: Char, condition: (String) -> Boolean) {
if (isNotEmpty() && condition(last()!!)) storage[storage.size - 1] += char.toString()
else push(char)
}
I have this:
val navigateToMainFragmentEvent: StateFlow<State<Event<Boolean>>>
if (navigateToMainFragmentEvent.collectAsState().value is State.TriggerState) {
(viewModel.navigateToMainFragmentEvent.collectAsState().value
as State.TriggerState).data.getContentIfNotHandled()
?.let {
if (it) {
Timber.tag("Nurs").d("collect as state ")
navController.popBackStack()
navController.navigate(MAIN_SCRENN)
}
}
}
is it possible to shorten with generics the if statement?
val state = navigateToMainFragmentEvent.value
if (state is State.TriggerState) {
state.data.getContentIfNotHandled()?.let {
// do sth
}
}
As an advice: You can define ifNotHandled method with a lambda argument in your Event class to more shortening:
fun ifNotHandled(callback: () -> T) {
if (!hasBeenHandled) {
hasBeenHandled = true
callback.invoke(content)
}
}
val state = navigateToMainFragmentEvent.value
if (state is State.TriggerState) {
state.data.ifNotHandled {
// do sth
}
}
Try in rxJava2 Kotlin combine Single with Flowable but nothing not happening:
Does not undrstand what wrong
Flowable.create<Int>({ emmit ->
loadNewListener = object :Listener {
override fun onEmit(id: Int) {
emmit.onNext(id)
}
}
}, BackpressureStrategy.LATEST)
.debounce(500, TimeUnit.MILLISECONDS)
.flatMapSingle {
loadNew(id = it.id)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ (data:Data) ->
}, {
Timber.e("Failed load data ${it.message}")
})
my method is returning Single:
private fun loadNew(id: Int): Single<Data> {
return when (pdfType) {
CASE_0 -> {
Single.create<Data> { emmit ->
service.get("data")
.enqueue(
object : Callback<Void> {
override fun onFailure(call: Call<Void>?, t: Throwable?) {
// failure
}
override fun onResponse(call: Call<Void>?, response: Response<Void>?) {
emmit.onSuccess(it.data)
}
}
}//single
}//case_0
CASE_1 -> 1Repository.loadsome1Rx(id = id).map { it.getData() }
CASE_2 -> 2Repository.loadsom2LocalRx(id = id).map { it.getData() }
else -> {
throw java.lang.RuntimeException("$this is not available type!")
}
}
What is wrong im my code?
Need Maby call Single in Flowable subscribe() seppurate
like this?
Flowable.create<Int>({ emmit ->
loadNewListener = object :Listener {
override fun onEmit(id: Int) {
emmit.onNext(id)
}
}
}, BackpressureStrategy.LATEST)
.debounce(500, TimeUnit.MILLISECONDS)
.subscribe({
loadNew(id = it.id)
}, {
Timber.e("")
})
This code is workin but looks not simple as via combine try.
This simple example based on your code is working
var i = 0
fun foo() {
Flowable.create<Int>({ emmit ->
emmit.onNext(i)
i++
}, BackpressureStrategy.LATEST)
.debounce(500, TimeUnit.MILLISECONDS)
.flatMapSingle {
Single.create<String> { emmit ->
emmit.onSuccess("onSuccess: $it")
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
Log.i("RX", "Subscribe: $it")
}, {
it.printStackTrace()
})
}
Check SingleEmitter.onSuccess() and SingleEmitter.onError() is called in all cases in when (pdfType)...
As #Stas Bondar said in answer below This simple example based on your code is working!!
Problem was in loadNewListener .
It does not init in time and has null value when need. Call create Flowable on init ViewModel but loadNewListener did not have time to create when i call him from fragment.
loadNewListener = object :Listener{...}
Becuse need some time mutch for init rxJava expression!
And combine flowable with single via flatMapSingle spent more time than just call single on flowable dubscrinbe!
So use temp field:
private var temp: Temp? = null
fun load(id: Int) {
loadNewListener.apply {
when {
this != null -> load(id = id)
else -> userEmitPdfTemp = Temp(id = id)
}
}
}
Flowable.create<Data>({ emmit ->
userEmitPdfTemp?.let {id->
emmit.onNext(Data(id))
userEmitPdfTemp =null
}
loadNewListener = object :Listener {
override fun load(id: Int) {
emmit.onNext(Data(id))
}
}
}
Why the following does compile in Kotlin:
fun foo(): Boolean {
while (true) {
return true
}
}
fun bar(): Boolean {
synchronized("") {
return foo()
}
}
while the following doesn't?
fun baz(): Boolean {
synchronized("") {
while (true) {
return true
}
}
}
And what is the idiomatic way to make the latter function to compile? I can rewrite it as follows:
fun baz(): Boolean {
synchronized("") {
while (true) {
return true
}
TODO("Never")
}
}
-- but it doesn't look elegant enough.