How would I write this in idiomatic Kotlin? - 2 - kotlin

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)

Related

Kotlin ? vs ?.let {}

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

What is the elegant way of for loop with condition to add to a list in Kotlin

What is the more elegant way of doing the following code in Kotlin
fun bar(bars:List<Bar>): List<Foo>{
val foos = mutableListOf<Foo>()
for(bar in bars){
val foo = foo(bar)
if(foo != null){
foos.add(foo)
}
}
return foos
}
fun foo(bar:Bar): Foo?{
if(bar.something){
return null
}
return Foo()
}
bar() can be rewritten to use mapNotNull():
fun bar(bars: List<Bar>) = bars.mapNotNull{ foo(it) }
or (using a method reference):
fun bar(bars: List<Bar>) = bars.mapNotNull(::foo)
And foo() could also be written with an expression body:
fun foo(bar: Bar) = if (bar.something) null else Foo()
(I've omitted the return types too, as the compiler easily infers them — though you may want to keep them for extra safety/readability.)
Both would also work well as extension functions:
fun List<Bar>.bar() = mapNotNull{ it.foo() }
fun Bar.foo() = if (something) null else Foo()
The whole thing can be simplified to:
bars.filterNot { it.something }.map { Foo() }
This is because you are doing two things:
foo returns Foo() if a certain property is false, otherwise returns null
bar filters out the non-null results.
So what you want are Foo objects for every bar where Bar.something is false, which is what this does.
Working example:
class Foo
data class Bar(val something: Boolean)
fun List<Bar>.toFoos(): List<Foo> = filterNot { it.something }.map { Foo() }
fun main() {
val input = listOf(Bar(true), Bar(false), Bar(false), Bar(true), Bar(true))
val output = input.toFoos()
println(output)
}
Output;
[Foo#4a574795, Foo#f6f4d33]
U could use mapNotNull().
val foos = bars.mapNotNull { foo(it) }
Hope its elegant enough.
These two functions could be replaced with the following one-liner:
fun bar(bars:List<Bar>): List<Foo> = Array(bars.count { !it.something }) { Foo() }.asList()
the elegent way is to write it in a functional way :
fun bar(bars: List<Bar>): List<Foo> {
return bars.filter { !it.something }.map { Foo() }
}

What is the difference between Comparable and operator compareTo?

Lets say I have class A(val foo: Double).
I want to be be able to compare it to other A, Double, and Int.
If I implement Comparable, I can only compare it against one other object type.
override fun compareTo(other: A): Int {
return when {
this.foo == other.foo -> 0
this.foo > other.foo -> 1
else -> -1
}
}
But I've also seen extension functions overriding the compareTo operator.
operator fun A.compareTo(d: Double): Int {
return when {
this.foo == d -> 0
this.foo > d -> 1
else -> -1
}
}
What is the difference between these and what should I be using? I'm guessing if I want to compare it to multiple types then I have to use extension functions?
The Comparable interface comes from Java, and, as you have seen, is defined with only a compareTo( other) method, which only provides for comparing an object to another object of the same type.
As you have also noticed, the Kotlin extension functions are additional functions allowing you to compare an object to whatever you want, as long as you define the compareTo method to take an argument of the type to which you want to compare.
So, yes, if you want to compare an object to an object of a different type, you will need to write an appropriate extension function.
Of course, in Java, if you have control of the source code of the class, you can always add a custom compareTo method.
Comparable is a standard interface, it's the way you define a class as having some ordering, and every library that deals with ordering works with Comparable types. Basically, if you want to be able to order and compare your things using all the standard functions and anything anyone else might write, you need to implement the Comparable interface.
This works:
data class SportsTeam(val name: String) : Comparable<SportsTeam> {
override fun compareTo(other: SportsTeam): Int = when {
name == "best team" -> 1
other.name == "best team" -> -1
else -> 0
}
}
fun main(args: Array<String>) {
val best = SportsTeam("best team")
val worst = SportsTeam("worst team")
print("The winner is: ${maxOf(best, worst).name}")
}
but because maxOf takes a Comparable type, this won't work:
data class SportsTeam(val name: String)
fun SportsTeam.compareTo(other: SportsTeam): Int = when {
name == "best team" -> 1
other.name == "best team" -> -1
else -> 0
}
fun main(args: Array<String>) {
val best = SportsTeam("best team")
val worst = SportsTeam("worst team")
print("The winner is: ${maxOf(best, worst).name}")
}
Wenn you make your own objects you must implement Comparable interface and then override compareTo function
class MyClass : Comparable<MyClass> {
override fun compareTo(other: MyClass): Int {
// TODO Returns zero if this object is equal to the specified other object
}
}
You can also override an operator function, for example from Int class in kotlin
fun main(args: Array<String>) {
val a = 1
val b = "2"
println(a.compareTo(b))
}
operator fun Int.compareTo(i: String) : Int {
return if (this.toString() == i) {
0
} else {
1
}
}
I hope that's helpfull from you

Kotlin - Why do we have to explicit type parameter(s) for generic method?

I'm working on extension method like this:
infix fun <T> T.isNullOr(other: T): Boolean {
if (this == null) return true
return this == other
}
and I'm trying to use this method like this.
val thisShouldWork = true isNullOr true // this is true
val thisShouldNotWork = true isNullOr 0 // No compilation errors?
I expected compilation error because type parameter is automatically set to Boolean for isNullOr but it wasn't. What's happening?
am I misunderstanding about it?
in C#, same code working well as I expected.
static bool IsNullOr<T>(this T t, T other) {
if (t == null) return true;
return Equals(t, other);
}
bool howAboutThis = 0.IsNullOr(0);
bool andThis = 0.IsNullOr(false); // error - cannot detect type parameter for this
Here, val thisShouldNotWork = true isNullOr 0 is equal to val thisShouldNotWork: Boolean = true.isNullOr<Any>(0). Type parameter as inferred as the closest parent.
And function's return type is based on logical expression evaluation: this == other. Let's see == function declaration: public open operator fun equals(other: Any?): Boolean. It receives Any?.
Type parameter in this function has nothing to do with Boolean.
Just remember that generic type information is erased at runtime and whenever you try to put something into a method that accepts generics, then the common denominator is assumed, e.g.:
listOf("one", 123) // -> assumes T:Any and therefore gives List<Any>
Now for your example that would mean "one".isNullOr(123) both become Any.
As a sidenote however, if you declare a specific type (e.g. List<String>) as shown next, it will not work to assign a different type to it:
val test : List<String> = listOf(123) // this will not work
It is already known at compile time that the given int can't become a string. This sample however doesn't help you as you do not return that generic type. If your method just looked a bit different, e.g. would have a generic type as return value, it might easily have worked out similar to the List-sample before.
So to fix your sample you need to specify the type which will basically make the infix obsolete, e.g. the following will work as you expect:
val someString : String? = TODO()
val works = someString.isNullOr<String?>("other")
val doesntWork = someString.isNullOr<Int?>(123) // does not nor does:
val doesntWorkToo = someString.isNullOr<String?>(123)
Note that for what you've shown some standard functionality might help you (but not eliminate that specific problem), i.e. using the ?: (elvis operator) with a ?.let:
val someVal : String? = "someString given from somewhere"
val thisWorks = someVal?.let {
it == "some other string to compare"
} ?: true /* which basically means it was null */
val thisWillNot = someVal?.let {
it == 123 // compile error (funny enough: it.equals(123) would work ;-)
} ?: true /* it is null */
I think in this case the generics don't really matter. You only call equals in the method, which you can do on any type. It's basically the same as:
infix fun Any.isNullOr(other: Any): Boolean {
return this == other
}
It compiles without problems because you can always call equals with anything: other: Any?
Thank for answers. I think there is no way to prevent this at compilation level, so I decided to check type for other.
inline infix fun <reified T> T.isNullOr(other: T): Boolean {
if (this == null) return true
if (other !is T) return false
return this == other
}
If you really want to prevent it, you can:
class IsNullOr<T>(val x: T) {
operator fun invoke(other: T): Boolean {
if (x == null) return true
return x == other
}
}
fun <T> T.isNullOr() = IsNullOr(this)
fun main(args: Array<String>) {
val thisShouldWork = true.isNullOr()(true) // compiles
val thisShouldNotWork = true.isNullOr()(0) // doesn't compile
}
This makes type inference depend only on the receiver of isNullOr. If vals could be generic, you'd even keep the original syntax (but they can't).

How would I write this in idiomatic Kotlin?

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.