8 ms·
Java version in the article: public int parseAndInc(String number) { return Optional.ofNullable(number) .map(Integer::parseInt)
by V-2 8y ago
Java version in the article:
public int parseAndInc(String number) {
return Optional.ofNullable(number)
.map(Integer::parseInt)
.map(it -> it + 1)
.orElse(0);
}
And the Kotlin equivalent...
"No problem one might say, in Kotlin, for mapping you can use the let function:
fun parseAndInc(number: String?): Int {
return number.let { Integer.parseInt(it) }
.let { it -> it + 1 } ?: 0
}
Can you? Yes, but it’s not that simple. The above code is wrong and throws NPE from parseInt()."
Yes - that's why Kotlin standard library provides, among many others, a convenient extension function String.toInt(), which is a wrapper around Integer.parseInt().
You're supposed to use that, and then you're safe from NPE, because the receiver has to be explicitly non-nullable (it's String.toInt(), not String?.toInt()).
Also there is no need to redundantly repeat "it" inside the lambda - plus you could further simplify the implementation by using type inference and converting it to expression body:
fun parseAndInc(number: String?) = number
?.toInt()
?.let { it + 1 }
?: 0
"Now, compare readability of the Java and Kotlin versions. Which one do you prefer?"
Well, I'll say orElse(0) is more readable than "?: 0" dangling at the end of the expression. However Java is more verbose. I'd say it's a matter of taste.
The difference is that thanks to Kotlin's features - which allow for creating custom DSL easily - implementing orElse is trivial, if you can't live without it:
fun Int?.orElse(fallback: Int) = this ?: fallback
This allows for:
fun parseAndInc(number: String?): Int = number
?.toInt()
?.let { it + 1 }
.orElse(0)
And this, to me, is already nicer than your Java version.
- kodablah 8y agoOr the one I posted below: fun parseAndInc(number: String?) = number?.toInt()?.inc() ?: 0
- V-2 8y agoOh yeah, I forgot about inc().
- ptx 8y agoTo me it seems really weird to have the function take a nullable string in the first place (and not obvious that null + 1 == 0), so the right solution is probably to deal with the null values at some earlier stage – but in the interest of further bikeshedding of the implementation, a simple if-expression is pretty clean: fun parseAndInc(number: String?) = if (number != null) number.toInt() + 1 else 0
- V-2 8y agoOr fun parseAndInc(number: String?) = (number ?: "-1").toInt() + 1 ;)
- Sharlin 8y agoSo you can emulate map with language features like that. But what about other combinators? filter, flatMap/andThen, etc? Plus of course combinators have the advantage that once you realize that optionals, lists, futures and so on have a common structure, you only need to learn the interface once.
- V-2 8y agofilter, flatMap etc. are provided as extension functions for collections - see http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/index.html http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collectio...
- Sharlin 8y agoYeah, but to me not having Optional with the same interface, and instead language-level syntax for working with nullables, does smell like bad design.
- V-2 8y agoTo me java.util.Optional is a crutch; an afterthought. I think it would never come to exist if nullability was done right to begin with. Native approach feels cleaner... to me, of course. For starters - in the context of the quoted code example - what sense does it make to have to allocate a new object (via Optional.ofNullable) every single time parseAndInc calculation is performed?? Wrapper types always involve overhead... perfectly avoidable with a language-level, as opposed to library-level, implementation
- Sharlin 8y agoOptional/Option/Maybe is the proper way to implement "may not exist". Null is a crutch and language features that try to make it more reasonable to use are crutches on top of crutches. But I agree that in a language that already has null and can't get rid of it, Optional cannot ever be as useful as in a language that was designed properly in the first place. Wrapper types currently incur overhead in Java. If/when value types are finally implemented, this overhead will become negligible. In languages such as Rust and C++, Optional is already a zero-cost abstraction. From a type theoretic point of view it's clear that Optional has a monad structure, and all the usual combinators make perfect sense for it and are actually a joy to use in languages that support them, including Java. After all, it's perfectly isomorphic to a list with either 0 or 1 elements.
- wtetzner 8y ago> fun Int?.orElse(fallback: Int) = this ?: fallback Or even better: fun <T> T?.orElse(fallback: T) = this ?: fallback