Simple Functions
Take a look at function syntax and make the function start return the string "OK".
In the tasks the function TODO() is used that throws an exception.
Your job during the koans will be to replace this function invocation with
a meaningful code according to the problem.
Solution
funstart() ="OK"
Java to Kotlin conversion
We have a handy tool for Java developers: Java to Kotlin converter.
It works better in Intellij, but you can try it online as well.
To become familiar with it, please convert the code below.
Copy Java code, choose 'Convert from Java' above and copy the result function back.
public class JavaCode {
public String toJSON(Collection<Integer> collection) {
StringBuilder sb = new StringBuilder();
sb.append("[");
Iterator<Integer> iterator = collection.iterator();
while (iterator.hasNext()) {
Integer element = iterator.next();
sb.append(element);
if (iterator.hasNext()) {
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
}
Solution
funtoJSON(collection:Collection<Int>): String {
val sb =StringBuilder();
sb.append("[");
val iterator = collection.iterator();
while (iterator.hasNext()) {
var element = iterator.next();
sb.append(element);
if (iterator.hasNext()) {
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
Named arguments
Default and named arguments help to minimize the number of overloads
and improve the readability of the function invocation.
The library function joinToString is declared with default values for parameters:
fun joinToString(
separator: String = ", ",
prefix: String = "",
postfix: String = "",
/* ... */
): String
It can be called on a collection of Strings. Using named arguments
make the function joinOptions() return the list in a JSON format (e.g., "[a, b, c]")
There are several overloads of 'foo()' in Java:
public String foo(String name, int number, boolean toUpperCase) {
return (toUpperCase ? name.toUpperCase() : name) + number;
}
public String foo(String name, int number) {
return foo(name, number, false);
}
public String foo(String name, boolean toUpperCase) {
return foo(name, 42, toUpperCase);
}
public String foo(String name) {
return foo(name, 42);
}
All these Java overloads can be replaced with one function in Kotlin.
Change the declaration of the function foo in a way that makes the code
using foo compile. Use default and named arguments.
Kotlin supports a functional style of programming. Read about higher-order
functions and function literals (lambdas) in Kotlin.
Pass a lambda to any function to check if the collection contains an even number.
The function any gets a predicate as an argument and returns true
if there is at least one element satisfying the predicate.
Solution
funcontainsEven(collection:Collection<Int>): Boolean= collection.any { it %2==0 }
Strings
Read about different string literals and string templates in Kotlin.
Raw strings are useful for writing regex patterns, you don't need to escape
a backslash by a backslash. Below there is a pattern that matches a date
in format dd.mm.yyyy; in a usual string you'd have to write "(\\d{2})\\.(\\d{2})\\.(\\d{4})".
fun getPattern() = """(\d{2})\.(\d{2})\.(\d{4})"""
Using month variable rewrite this pattern in such a way that it matches
the date in format 13 JUN 1992.
Solution
val month ="(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)"fungetPattern() ="""(\d{2}) ${month}"""
Data classes
Convert the following Java code to Kotlin:
public static class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
Then add an annotation data to the resulting class. This annotation means
the compiler will generate a bunch of useful methods in this class:
equals/hashCode, toString and some others. The getPeople function should start to compile.
Then read about classes, properties and data classes in more detail.
Read about null safety and safe calls in Kotlin and rewrite the following
Java code using only one if expression:
public void sendMessageToClient(
@Nullable Client client,
@Nullable String message,
@NotNull Mailer mailer
) {
if (client == null || message == null) return;
PersonalInfo personalInfo = client.getPersonalInfo();
if (personalInfo == null) return;
String email = personalInfo.getEmail();
if (email == null) return;
mailer.sendMessage(email, message);
}
Solution
funsendMessageToClient(
client:Client?, message:String?, mailer:Mailer
){
val personalInfo = client?.personalInfo
val email = personalInfo?.email
if (email !=null&& message !=null)
mailer.sendMessage(email,message)
}
classClient (valpersonalInfo:PersonalInfo?)
classPersonalInfo (valemail:String?)
interfaceMailer {
funsendMessage(email:String, message:String)
}
Smart casts
Smart casts
Rewrite the following Java code using smart casts and when expression:
public int eval(Expr expr) {
if (expr instanceof Num) {
return ((Num) expr).getValue();
}
if (expr instanceof Sum) {
Sum sum = (Sum) expr;
return eval(sum.getLeft()) + eval(sum.getRight());
}
throw new IllegalArgumentException("Unknown expression");
}
Read about extension functions. Then implement extension functions Int.r()
and Pair.r() and make them convert Int and Pair to RationalNumber.
Solution
fun Int.r(): RationalNumber=RationalNumber(this,1)
funPair<Int, Int>.r(): RationalNumber=RationalNumber(this.first, this.second)
funPair<Int, Int>.r(): RationalNumber=RationalNumber(this.component1(), this.component2())
data classRationalNumber(valnumerator:Int, valdenominator:Int)
Object expressions
Read about object expressions that play the same role in Kotlin as anonymous
classes do in Java.
Add an object expression that provides a comparator to sort a list in a descending
order using java.util.Collections class. In Kotlin you use Kotlin library extensions
instead of java.util.Collections, but this example is still a good demonstration of
mixing Kotlin and Java code.
Solution
importjava.util.*fungetList(): List<Int> {
val arrayList =arrayListOf(1, 5, 2)
[
Collections.sort(arrayList, object:Comparator<Int> {
overridefuncompare(x:Int, y:Int) = y - x
})
ORCollections.sort(arrayList, { x, y -> y - x })
ORCollections.sort(arrayList, {x:Int, y:Int-> y - x})
]
return arrayList
}
return arrayList
}
SAM conversions
SAM conversions
When an object implements a SAM interface (one with a Single Abstract Method),
you can pass a lambda instead. Read more about SAM conversions in the blog posts
about Kotlin. At first, SAM-constructors were introduced, then SAM-conversions
were finally supported.
In the previous example change an object expression to a lambda.
Solution
fungetList(): List<Int> {
val arrayList =arrayListOf(1, 5, 2)
Collections.sort(arrayList, { x, y -> y - x })
return arrayList
}
Extensions on collections
Extension functions on collections
Kotlin code can be easily mixed with Java code. Thus in Kotlin we don't
introduce our own collections, but use standard Java ones (slightly improved).
Read about read-only and mutable views on Java collections.
In Kotlin standard library there are lots of extension functions that make
the work with collections more convenient. Rewrite the previous example once
more using an extension function sortedDescending.
This part was inspired by GS Collections Kata.
Default collections in Kotlin are Java collections, but there are lots of useful extension functions for them. For example, operations that transform a collection to another one, starting with 'to': toSet or toList.
Implement an extension function Shop.getSetOfCustomers(). The class Shop and all related classes can be found at Shop.kt.
Solution
fun Shop.getSetOfCustomers(): Set<Customer> {
returnthis.customers.toSet()
}
Filter Map
Implement extension functions Shop.getCitiesCustomersAreFrom() and Shop.getCustomersFrom() using functions map and filter.
val numbers = listOf(1, -1, 2)
numbers.filter { it > 0 } == listOf(1, 2)
numbers.map { it * it } == listOf(1, 1, 4)
Solution
fun Shop.getCitiesCustomersAreFrom(): Set<City> = customers.map({it.city}).toSet()
fun Shop.getCustomersFrom(city:City): List<Customer> = customers.filter({it.city === city}).toList()
All, Any, Count, FirstOrNull
Implement all the functions below using all, any, count, firstOrNull.
val numbers = listOf(-1, 0, 2)
val isZero: (Int) -> Boolean = { it == 0 }
numbers.any(isZero) == true
numbers.all(isZero) == false
numbers.count(isZero) == 1
numbers.firstOrNull { it > 0 } == 2
Solution
fun Shop.checkAllCustomersAreFrom(city:City): Boolean= customers.all(givenCity(city))
fun Shop.hasCustomerFrom(city:City): Boolean= customers.any(givenCity(city))
fun Shop.countCustomersFrom(city:City): Int= customers.count(givenCity(city))
fun Shop.findAnyCustomerFrom(city:City): Customer?= customers.firstOrNull(givenCity(city))
privatefungivenCity(city:City): (Customer) ->Boolean= { it.city === city }
FlatMap
Implement Customer.getOrderedProducts() and Shop.getAllOrderedProducts() using flatMap.
val result = listOf("abc", "12").flatMap { it.toCharList() }
result == listOf('a', 'b', 'c', '1', '2')
Solution
fun Customer.getOrderedProducts(): Set<Product> = orders.flatMap({it.products}).toSet()
fun Shop.getAllOrderedProducts(): Set<Product> = customers.flatMap({it.getOrderedProducts()}).toSet()
请发表评论