This question already has answers here:
Swift 'if let' statement equivalent in Kotlin
(16 answers)
Closed 4 years ago.
let say I have this variable
val number : Int? = 12
I want to make control flow based on the variable, if the variable is null then do something, otherwise do something else
in Swift I can make something like this:
if let number = number {
print(number)
} else {
// do something else
}
I actually can do like this
if number != null {
print(number!!) // I want to avoid exclamation mark like this
} else {
// do something else
}
but I want to avoid exclamation mark, like in print(number!!)
I previously though that I can do something like this
number?.let {
print(it)
} else {
// else block is not available in let in Kotlin
}
so how to solve this ?
The ?. means it will be executed only if the left side is not null.
The ?: operator executes the right side only if the left side is not null.
You may have something like:
theExpression().toCompute().theNumber?.let { number ->
print("This is my number $number
} ?: run {
print("There is no number")
}
Here we use T.let extension function for the then clause and run{ } (extension) function for the else clause.
Warning: The semantics is that you expected to return non-null value from the let {...} closure to avoid the `run {..} closure from being executed. Thus the code like:
number?.let { null } ?: run { 42 } === 42 (and not null)
Related
This question already has answers here:
Regular Expression In Android for Password Field
(13 answers)
Closed 1 year ago.
I'm new in Kotlin and trying to find the most elegant solution of password validation with conditions:
Password must be at least 8 characters.
It must have at least 1 lowercase and at least 1 uppercase letter.
It must have one special character like ! or + or - or similar
It must have at least 1 digit
"Elegant" is subjective!
Here's a functional approach to it:
// you can define each rule as a separate checking function,
// adding more doesn't change the complexity
fun String.isLongEnough() = length >= 8
fun String.hasEnoughDigits() = count(Char::isDigit) > 0
fun String.isMixedCase() = any(Char::isLowerCase) && any(Char::isUpperCase)
fun String.hasSpecialChar() = any { it in "!,+^" }
// you can decide which requirements need to be included (or make separate lists
// of different priority requirements, and check that enough of each have been met)
val requirements = listOf(String::isLongEnough, String::hasEnoughDigits)
val String.meetsRequirements get() = requirements.all { check -> check(this) }
fun main() {
val password = "hiThere2!+"
println(password.meetsRequirements)
}
I think the benefit is it's easy to add new rules and they can be very simple and readable, and you can handle the validation logic in a separate step (e.g. if you're implementing a "password strength" metric, where meeting some requirements counts more than others).
I've used some fancier language features in there, but it's to keep it concise really. The String.whatever() extension functions just mean you don't need to reference the string parameter in the function (it's this), and the function references (String::hasEnoughDigits) let you do that requirements.all call instead of going if (isLongEnough(password) && hasEnoughDigits(password) && ...) and so on. You could do it that way if you wanted!
Lots of options and ways to approach it. Regexes can definitely be elegant, but they can also be hard to work with
You can do like this ...
internal fun isValidPassword(password: String): Boolean {
if (password.length < 8) return false
if (password.filter { it.isDigit() }.firstOrNull() == null) return false
if (password.filter { it.isLetter() }.filter { it.isUpperCase() }.firstOrNull() == null) return false
if (password.filter { it.isLetter() }.filter { it.isLowerCase() }.firstOrNull() == null) return false
if (password.filter { !it.isLetterOrDigit() }.firstOrNull() == null) return false
return true
}
How does the Kotlin compiler decide whether an expression enclosed in { } is a block or a lambda?
Consider this:
val a: Int
if (cond)
a = 1
else
a = 2
can be written more succinctly as:
val a =
if (cond)
1
else
2
Similarly, one might think that this:
val a: () -> Int
if (cond)
a = { 1 }
else
a = { 2 }
should be able to be written more succinctly like this:
val a =
if (cond)
{ 1 }
else
{ 2 }
But this is not the same: a is now an Int, and not of type () -> Int, because now the { 1 } is no longer a lambda. What are the rules that say whether something is a lambda or a block?
I didn't look into the Kotlin lexer, but I guess there are few possible places in the code where the compiler expects either a single expression or a block of code. That includes the code immediately following most of control flow statements like: if, else, while, when (one of its cases), etc. If you put { in one of these places, it will be interpreted as a start of the block of code related to this control flow statement and not as a lambda.
This is as simple as that. Note that even if you hint the compiler about the type, it still won't work:
// compile error
val a: () -> Int = if (cond)
{ 1 }
else
{ 2 }
It will be interpreted more like this:
// compile error
val a: () -> Int = if (cond) {
1
} else {
2
}
{ after if condition is always interpreted as a start of block of code. You need to put double {, } in cases like this:
// works fine
val a: () -> Int = if (cond) {
{ 1 }
} else {
{ 2 }
}
To put it very succinctly and easy to remember, the first opening brace after an if/when/else/for is always assumed to be the opening of a block. Use double braces if you want a lambda in there.
This is specified in the Kotlin Language Specification, section 1.2: Syntax Grammar:
The grammar defines a block as statements between { and }. In most cases (such as function bodies) the syntax is different between a block and a lambda. Where it is the same is in the usage of controlStructureBody - these are the places where the block has a value, or where you could put a non-block expression in its place. If you search the whole spec document for "controlStructureBody", you'll find it's used in the following places:
For statement
While statement
Do-while statement
If expression
When expression
When entry
In every other place where a value is required, a '{' signifies the start of a lambda.
I want to make an extension function(named (printDatatype) for example) that can be applied on all datatypes and just prints it ... for example :
"example".printDatatype() ==>output: example
56.prinDatatype()==>output: 56
null.printDatatype()==>output: null
className.printDatatype()==> output: ClassName(property1 = value, property2 = value ....)
Soo something like this?
fun Any?.printLn() = println(this)
Any custom objects will need to override the toString method (like always). That will be auto on data classes.
I honestly don't know what the use case for something like would be.
so i figured it out :)
fun Any?.printMe() {
if (this is Class<*>) {
println(this.toString())
} else if (this.toString() == "null"){
println("null")
}
else {
println(this)
}
}
In the sample below, the function should return a non-null data.
Since the data could be changed in the process, it needs to be var, and can only be nullable to start with.
I can't use lateinit because the first call of if (d == null) will throw.
After the process it will be assigned a non-null data, but the return has to use the !! (double bang or non-null assertion operator).
What is the best approach to avoid the !!?
fun testGetLowest (dataArray: List<Data>) : Data {
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return d!!
}
If you don't like !! then supply a default value for it. You'll realize you can only supply the default value if the list is not empty, but, as you said, the list is already known to be non-empty. The good part of this story is that the type system doesn't track list size so when you say dataArray[0], it will take your word for it.
fun testGetLowest(dataArray: List<Data>) : Data {
var d: Data = dataArray[0]
for (i in 1 until dataArray.size) {
if (d.level < dataArray[i].level) {
d = dataArray[i]
}
}
return d
}
Normally, you can and should lean on the compiler to infer nullability. This is not always possible, and in the contrived example if the inner loop runs but once d is non-null. This is guaranteed to happen if dataArray has at least one member.
Using this knowledge you could refactor the code slightly using require to check the arguments (for at least one member of the array) and checkNotNull to assert the state of the dataArray as a post-condition.
fun testGetLowest (dataArray: List<Data>) : Data {
require(dataArray.size > 0, { "Expected dataArray to have size of at least 1: $dataArray")
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return checkNotNull(d, { "Expected d to be non-null through dataArray having at least one element and d being assigned in first iteration of loop" })
}
Remember you can return the result of a checkNotNull (and similar operators):
val checkedD = checkNotNull(d)
See Google Guava's Preconditions for something similar.
Even if you were to convert it to an Option, you would still have to deal with the case when dataArray is empty and so the value returned is undefined.
If you wanted to make this a complete function instead of throwing an exception, you can return an Option<Data> instead of a Data so that the case of an empty dataArray would return a None and leave it up to the caller to deal with how to handle the sad path.
How to do the same check, and cover the empty case
fun testGetLowest(dataArray: List<Data>)
= dataArray.minBy { it.level } ?: throw AssertionError("List was empty")
This uses the ?: operator to either get the minimum, or if the minimum is null (the list is empty) throws an error instead.
The accepted answer is completly fine but just to mentioned another way to solve your problem by changing one line in your code: return d ?: dataArray[0]
For simple check like
if (variable != null) {
doSomething(variable)
}
We could change to
variable?.let { doSometing(it) }
However for a case with else
if (variable != null) {
doSomething(variable)
} else {
doOtherThing()
}
Is there a way of doing so in a single function? Is there something like either?
You can use the elvis-operator ?: like so:
variable?.let { doSomething(it) } ?: doOtherThing()
However personally I think that this gets quite unreadable very quickly. There is nothing wrong with an if-expression.
Another approach which might ring well with functional style is the use of when, which is very close to pattern matching:
when(variable) {
null -> doOtherThingSomething()
else -> doSomething(variable)
}
I would argue, however, that you should be careful with abandoning the classic if statement, if what you're after is its natural purpose - conditional execution. If you're calculating a value in two different ways and then using it in the same way, this approach makes sense. But if your code paths diverge at this point and don't meet again, then if provides a clearer intent.
You can map null-able value if not null by using ternary operator to check not null condition with If...Else Statements.
Here, I had wrote some code snippet to check value null or not ,
Case 1: value initialized
fun main(args: Array<String>) {
val value:Int ?= 10
val mapped = value?.let { "Value is == $value" } ?: "Value not initialized..."
println(mapped)
}
You gotta result: Value is == 10
Case 2: value set remains null
fun main(args: Array<String>) {
val value:Int ?= null
val mapped = value?.let { "Value is == $value" } ?: "Value not initialized..."
println(mapped)
}
You gotta result: Value not initialized...