surname: String
.
That's because in most cases, type is optional and you don't have to declare it explicitly.Object
type is represented in Kotlin by Any
typeUnit
instead of void
by default// Full declaration
val immutableVariable: String = "text"
var mutableVariable: String = "text"
// With type interference
val immutableVariable = "text"
var mutableVariable = "text"
// Full declaration
final String immutableVariable = "text"
String mutableVariable = "text"
// With type interference
final var immutableVariable = "text"
var mutableVariable = "text"
null
is quite painful value to handle.
To cover this design flaw in Java,
we can use Optional<T>
wrapper to make sure the absence of the value will be handled by user.
In Kotlin, instead of wrapping every value in such wrapper,
we can just simply add ?
symbol to type to mark it as nullable:val value: String? = "text"
// compilation error: cannot call isEmpty() on nullable type
// val isEmpty = value.isEmpty()
val isEmpty = value
?.isEmpty() // ?. operator calls method if 'value' is not null or returns null
?: true // ?: returns default value if previous expression was null
Option<String> value = Option.of("text")
boolean isEmpty = value
.map(text -> text.isEmpty())
.orElseGet(true)
// Creates immutable Map<String, String>
// Use mutableMapOf() for MutableMap
val map = mapOf(
// Syntax sugar 'Key to Value' creates instance of Pair<Key, Value>
"key1" to "value1",
"key2" to "value2"
)
val handled: String? = map["key3"]
val value: String = map["key3"] ?: "default"
Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
// Not really handled, you have to remember about its nullability
String nullable = map.get("key3");
// Usually we need to wrap a lot of such values
Option<String> handled = Option.of(map.get("key3"));
// We could use map.getOrDefault() in this case,
// but it's still quite rare scenario to see such extra methods.
// Usually, modern API returns Optional<T> for nullable responses,
// but Java can't change its API to keep compatibility,
// so it'll never get better at this point.
Option<String> value = Option.of(map.get("key3")).orElseGet("default");
class User(
val username: String,
var balance: Double = 0.00
) {
override fun toString(): String =
"$username ($balance USD)"
}
val user = User("Michael Scott") // no 'new' keyword
user.balance = 4.20 // Kotlin translates `setBalance` to `balance`
public final class User implements Serializable {
private final String username;
private final double balance;
public User(String name, String surname) {
this.name = name;
this.surname = surname;
}
public User(String username) {
this(username, 0.00)
}
public String getName() { return name; }
public Double getBalance() { return balance; }
public void setBalance(double newBalance) { this.balance = newBalance; }
@Override
public String toString() {
return String.format("%s (%s USD)", username, balance)
}
}
User user = new User("Dwight Schrute", 4.20)
user.setBalance(0)
fun method(): Any { return expression }
,
you can use = expression
operator to return this directly without a need to write standard body.
(Just like in the example above with toString() method)override
keywordUser(balance = 7, username = "Michael Scott")
.BiFunction<A, B, R>
),
you can just write (A, B) -> R
.val runnable: () -> Unit = { println("Reposilite") }
val consumer: (String) -> Unit = { println(it) }
val function: (Int) -> String = { it.toString() }
val biFunction: (Int, Int) -> Int = { a, b -> a + b }
Runnable runnable = () -> out.println("Reposilite");
Consumer<String> consumer = value -> out.println(value);
Function<Integer, String> function = value -> Integer.toString(value);
BiFunction<Integer, Integer, String> biFunction = (a, b) -> a + b;
it
variable in those lambdas.
You can think about it like a keyword in Kotlin that refers to the unnamed argument in single-parameter lambdas.
The most confusing part is often the we pass lambdas to functions:val hasMoney = Result.ok("10")
/* Single parameter */
.map({ it.toInt() }) // Standard parameter
.map() { it.toString() } // Simplify it by pulling the last lambda argument of out method
.map { it.toInt() } // Because () brackets are empty, you can just skip it
/* Multiple parameters */
.filter({ it > 0 }, { "Error: Negative balance" }) // Standard parameters
.filter({ it > 0 }) { "Error: Negative balance" } // Only the last lambda argument can be pulled out
.isOk()
boolean hasMoney = Result.ok("10")
.map((value) -> Integer.parseInt(value))
.filter(value -> value > 0, value -> "Error: Negative balance")
.isOk()
data class Request(val url: String, val ip: String)
// 'Request.' declares that we'll change context of given labda
// So 'this' will point to `Request` instance
fun handleRequest(callable: Request.() -> Unit) =
// Context argument is now the fist parameter in 'callable' function
callable.invoke(Request("reposilite.com", "127.0.0.1"))
// Usage of our DSL function
handleRequest {
println(this.url) // Explicit call using 'this.'
println(ip) // We can now use Request's properties directly
}
Function | Example | Result | Description |
---|---|---|---|
let | "10".let { it.toInt() } | 10 | Maps one value to another |
also | createUser().also { users.add(it) } | Result of createUser() | Consumes value and returns it as result |
with | with(user) { this.username } | Username | Maps value with DSL function |
panda.std.Result<Value, Error>
wrapper from
library to handle errors gracefully, without unexpected runtime exceptions.
For instance, instead of:fun createUser(username: String): User {
if (username.isEmpty()) {
throw IllegalArgumentException("Name cannot be empty")
}
return User(username)
}
Result<User, ErrorResponse>
:fun createUser(username: String): Result<User, ErrorResponse> =
username.asSuccess()
.filter({ it.isEmpty() }) { ErrorResponse(BAD_REQUEST, "Name cannot be empty") }
.map { User(it) }
Did you find misleading or deprecated content? Maybe you just feel this section misses important elements?
Copyright © 2023 dzikoysk with ❤ panda-lang