I have some code:
private fun getTouchX(): Int {
arguments ?: return centerX()
return if (arguments.containsKey(KEY_DOWN_X)) {
arguments.getInt(KEY_DOWN_X)
} else {
centerX()
}
}
private fun centerX() = (views.rootView?.width ?: 0) / 2
and I want to shorten it.
in the function getTouchX, there are two return conditions duplicated. (which is centerX)
I tried to do this:
private fun getTouchX(): Int {
if (arguments == null || !arguments.containsKey(KEY_DOWN_X)) {
return centerX()
}
return arguments.getInt(KEY_DOWN_X)
}
However, it looks more like Java than Kotlin.
How could I go about writing this in idiomatic Kotlin?
I'm not sure where arguments is coming from, but a cleaner solution would be
private fun getTouchX(): Int =
if(arguments?.containsKey(KEY_DOWN_X) == true) {
arguments.getInt(KEY_DOWN_X)
} else {
centerX()
}
The if only calls containsKey if arguments is non-null, otherwise the left side of == resolves to null. null != true, so it will return centerX() from else.
Similarly if arguments is non-null, then the result of containsKey will be used to resolve.
And now that there's only one expression, can use body expression format.
I'd probably go with an expression function with a when expression:
private fun getTouchX() = when {
arguments == null || !arguments.containsKey(KEY_DOWN_X) -> centerX()
else -> arguments.getInt(KEY_DOWN_X)
}
You could also consider declaring touchX as a private val:
private val touchX: Int
get() = when {
arguments == null || !arguments.containsKey(KEY_DOWN_X) -> centerX()
else -> arguments.getInt(KEY_DOWN_X)
}
Looking at just the plain Kotlin code, my suggestion would be:
private fun getTouchX() =
arguments?.let {
if (!it.containsKey(KEY_DOWN_X))
return#let null
it.getInt(KEY_DOWN_X)
} ?: centerX()
But if arguments is a descendent of an Android BaseBundle, you might further compress this to:
private fun getTouchX() = arguments?.getInt(KEY_DOWN_X, centerX()) ?: centerX()
Note: As the method signature suspiciously looks like reading a property, you might consider turning it into a read-only property.
Related
val customerInfo = when {
visitor.isCustomer -> customerService.getCustomerInfo(visitorId )
else -> null
}
In this Code, visitor.isCustomer is Boolean ( true / false)
Now then, I don't like specify else -> null into the code.
so i want to delete when statement and convert other ways..
How can I do that?
(I prefer to convert it with StandardKt (like let, apply, also... ))
You can just use an if/else
val customerInfo = if (visitor.isCustomer) customerService.getCustomerInfo(visitorId) else null
You could do something like
val customerInfo = vistorId.takeIf { visitor.isCustomer }?.let { customerService.getCustomerInfo(it) }
But I think a when or if statement is cleaner and more readable.
I think JetBrains coding convention would recommend an if statement instead of a when statement here.
Hope this will be more readable.
Without any additional things,
val customerInfo = if (visitor.isCustomer) customerService.getCustomerInfo(visitorId) else null
With your own extension functions
2)Without infix: (condition).ifTrueElseNull{ return value}
inline fun <T> Boolean?.ifTrueElseNull(block: () -> T): T? {
if (this == true) {
return block()
}
return null
}
var a = visitor.isCustomer.ifTrueElseNull{customerService.getCustomerInfo(visitorId)}
With infix: (condition) ifTrueElseNull{ return value}
inline infix fun <T> Boolean?.ifTrueElseNull(block: () -> T): T? {
if (this == true) {
return block()
}
return null
}
var a = visitor.isCustomer ifTrueElseNull{customerService.getCustomerInfo(visitorId)}
I have a function which has quite a lot lines. In that function I have a .filter{} like:
fun getMyListForFoo(): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter{ it.flag == Query.IS_FOO }
.map{
//..mappings
}
}
and then I have a second function just to retrieve queries which are NOT Foo:
fun getMyListForNotFoo(): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter{ it.flag != Query.IS_FOO }
.map{
//..mappings
}
}
As you can the only difference is the == or != operator in the .filter function. Although I have all the previous lines duplicated..
I bet there is a nice Kotlin way to enhance this code?
Pass a predicate as a parameter to your function for filtering the list.
fun getMyList(predicate: (YourType) -> Boolean): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter(predicate)
.map{
//..mappings
}
}
Usage:
val listForFoo = getMyList { it.flag == Query.IS_FOO }
val listForNotFoo = getMyList { it.flag != Query.IS_FOO }
OR, if you just want to pass a Boolean, you can also do that:
fun getMyList(filterFoo: Boolean): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter {
val isFoo = it.flag == Query.IS_FOO
if(filterFoo) isFoo else !isFoo
}
.map{
//..mappings
}
}
I would use partition directly.
I created a sample in kotlinlang.org's playground and it looks like this:
// Given a "thing"
data class Thing(val id: Int, val isFoo: Boolean)
// Have a function that simplifies this:
fun filterThings(source: List<Thing>) = source.partition { it.isFoo }
// Alternatively, you could have a more generic one:
fun filterThings(source: List<Thing>,
predicate: ((Thing) -> Boolean)) = source.partition(predicate)
// And you can use either like so:
// Given the source
val source = listOf(Thing(1, true),
Thing(2, true),
Thing(3, false),
Thing(4, true),
Thing(5, false),
Thing(6, false))
// Filter them with the non-configurable version:
val results = filterThings(source)
// or the more configurable one where *you* supply the predicate:
val results = filterThings(source) { it.isFoo }
The results are going to be:
results.first is going to be the one that pass the predicate, and the rest will be in results.second:
results.first = [Thing(id=1, isFoo=true), Thing(id=2, isFoo=true), Thing(id=4, isFoo=true)]
results.second = [Thing(id=3, isFoo=false), Thing(id=5, isFoo=false), Thing(id=6, isFoo=false)]
Consider this nice utility extension function i wanted to use :
inline infix fun <T> T?.otherwise(other: () -> Unit): T? {
if (this != null) return this
other()
return null
}
It could be very useful for logging stuff when expressions evaluated to null for example:
val x: Any? = null
x?.let { doSomeStuff() } otherwise {Log.d(TAG,"Otherwise happened")}
but I see that it wont work for :
val x: Any? = null
x?.otherwise {Log.d(TAG,"Otherwise happened")}
see here for running example
Well when thinking about it i guess that makes sense that if x is null the ? makes the postfix not be executed, but i dont understand why the let in the first example is any different?
Is it possible to fix the utility to be more robust and work without having to have let in the chain?
First, you can simplify the implementation:
inline infix fun <T> T?.otherwise(other: () -> Unit): T? {
if (this == null) { other() }
return this
}
Or
inline infix fun <T> T?.otherwise(other: () -> Unit): T? =
also { if (it == null) other() }
When you do this:
null?.otherwise { println("Otherwise happened") }
?. means "execute if not null", so otherwise is not executed.
What you need to write is:
null otherwise { println("Otherwise happened") }
Note this is very similar to the ?: operator (as Vadik pointed out in the comments):
null ?: println("Otherwise happened")
The difference is that otherwise always returns the value on the left (the same as also), but ?: returns the value on the right when the value on the left is null.
In my opinion, otherwise is confusing, especially as it always returns the left value despite the name. You would be better to use the ?: operator. Or perhaps rename it to something like alsoIfNull.
The let example executes because, when you don't utilize the infix feature, it looks like this:
x?.let {}.otherwise {println("1")}
Notice that it's not ?.otherwise; therefore, it always executes.
So to use otherwise without let, you can omit the ?.
x.otherwise { ... }
x?.let { doSomeStuff() }.otherwise {Log.d(TAG,"Otherwise happened")}
// ⬇️
val value = if (x != null) {
doSomeStuff()
} else {
null
}
value.otherwise {Log.d(TAG,"Otherwise happened")}
x?.otherwise { Log.d(TAG,"Otherwise happened") }
// ⬇️
if (x != null) {
otherwise { Log.d(TAG,"Otherwise happened") }
} else {
null
}
?. means if the value is not null then execute the method and return the result otherwise return null
I am tinkering around with Kotlin and I am trying to wrap my head around how nullable variables work in Kotlin. Here I have a piece of code that does a boolean check to see if a vehicle is over capacity. Is the implementation a good way to work with nullable variables or is there a more elegant way ?
class Route(var vehicle: Vehicle?, var jobs: List<Job>?) {
constructor()
constructor(vehicle: Vehicle?)
fun isOverCapacity() : Boolean {
val vehicleCapacity = vehicle?.capacity
if (vehicleCapacity != null){
val totalDemand = jobs?.sumBy { job -> job.demand }
if (totalDemand != null) {
return totalDemand > vehicleCapacity
}
}
return false
}
}
Thanks a lot!
fun isOverCapacity(): Boolean {
val vehicleCapacity = vehicle?.capacity ?: return false
val totalDemand = jobs?.sumBy { job -> job.demand } ?: return false
return totalDemand > vehicleCapacity
}
What does ?: do in Kotlin? (Elvis Operator)
By using kotlin std-lib dsl functional operators like let, run, also, apply, use.
Use of ?. -> if the object/value is not null then only call the next function.
let -> returns the result of lambda expression.
run -> returns the result of lambda expression passing this as receiver.
also -> does operation and returns itself unlike the result of lambda.
apply -> does operation and returns itself unlike the result of lambda passing this as receiver.
use -> returns the result of lambda expression and closes the Closeable resource.
You can simplify the code as follows:
fun isOverCapacity() : Boolean =
vehicle?.capacity?.let { vehicleCapacity ->
jobs?.sumBy { job -> job.demand }?.let { totalDemand ->
totalDemand > vehicleCapacity
}
} ?: false
I asked a question:
How would I write this in idiomatic Kotlin?
and now I had an idea for short this idiom. like below
private fun getTouchX(): Int = when(arguments)
containsKey(KEY_DOWN_X) -> getInt(KEY_DOWN_X)
else -> centerX()
}
containsKey and getInt are arguments's method.
Of course this is not correct idiom for when.
is there any possible way to do this?
arguments is Bundle class in Android framework.
you can see at below
https://developer.android.com/reference/android/os/Bundle.html
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/Bundle.java
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/BaseBundle.java
From the information you've provided I can only give you this answer:
private fun getTouchX(): Int = arguments.run {
if (containsKey(KEY_DOWN_X)) getInt(KEY_DOWN_X)
else centerX()
}
If arguments is nullable, it can be like this:
private fun getTouchX(): Int = arguments?.run {
if (containsKey(KEY_DOWN_X)) getInt(KEY_DOWN_X)
else null
} ?: centerX()
Thanks to #ice1000's answer.
I got that below idiom also possible
private fun getTouchX(): Int = arguments?.run {
when {
containsKey(KEY_DOWN_X) -> getInt(KEY_DOWN_X)
else -> null
}
} ?: centerX()
I might can use it when more than 3 predicate condition (if x 3)