Main characteristics – attractive language, including null safety, data classes, extension functions, and String templates.
1.Functions
Let’s define a Function having two Int parameters with Int return type:
1 | fun sum(a : Int, b : Int) : Int { return a + b } |
2. Defining Local Variables
Assign-once (read-only) local variable:
1 | val a : Int = 1 val b = 1 val c : Int c = 1 |
Note that type of a variable b is inferred by a Kotlin compiler. We could also define mutable variables:
12 | var x = 5 x + = 1 |
3. Optional Fields
Kotlin has basic syntax for defining a field that could be nullable (optional). When we want to declare that type of field is nullable we need to use type suffixed with a question mark:
1 | val email : String? |
When you defined nullable field it is perfectly valid to assign a null to it:
1 | val email : String? = null |
That means that in an email field could be a null. If we will write:
1 | val email : String = "value" |
Then we need to assign a value to email field in the same statement that we declare email. It can not have a null value. We will get back to Kotlin null safety in a later section.
4. Classes
Let’s demonstrate how to create a simple class for managing a specific category of a product. Our ItemManager class below has a default constructor that populates two fields — categoryId and dbConnection — and an optional email field:
1234 | class ItemManager( val categoryId : String, val dbConnection : String) { var email = "" // ... } |
That ItemManager(…) construct creates constructor and two fields in our class: categoryId and dbConnection
Note that our constructor uses the val keyword for its arguments — this means that the corresponding fields will be final and immutable. If we had used the var keyword (as we did when defining the email field), then those fields would be mutable.
Let’s create an instance of ItemManager using the default constructor:
1 | ItemManager( "cat_id" , "db://connection" ) |
We could construct ItemManager using named parameters. It is very useful when you have like in this example function that takes two parameters with the same type e.g. String, and you do not want to confuse an order of them. Using naming parameters you can explicitly write which parameter is assigned. In class ItemManager there are two fields, categoryId and dbConnection so both can be referenced using named parameters:
1 | ItemManager(categoryId = "catId" , dbConnection = "db://Connection" ) |
It is very useful when we need to pass more arguments to a function.
If you need additional constructors, you would define them using the constructor keyword. Let’s define another constructor that also sets the email field:
1234 | constructor(categoryId : String, dbConnection : String, email : String) : this (categoryId, dbConnection) { this .email = email } |
Note that this constructor invokes the default constructor that we defined above before setting the email field. And since we already defined categoryId and dbConnection to be immutable using the val keyword in the default constructor, we do not need to repeat the val keyword in the additional constructor.
Now, let’s create an instance using the additional constructor:
1 | ItemManager( "cat_id" , "db://connection" , "foo@bar.com" ) |
If you want to define an instance method on ItemManager, you would do so using the fun keyword:
123 | fun isFromSpecificCategory(catId : String) : Boolean { return categoryId == catId } |
5. Inheritance
By default, Kotlin’s classes are closed for extension — the equivalent of a class marked final in Java.
In order to specify that a class is open for extension, you would use the open keyword when defining the class.
Let’s define an Item class that is open for extension:
12345 | open class Item( val id : String, val name : String = "unknown_name" ) { open fun getIdOfItem() : String { return id } } |
Note that we also denoted the getIdOfItem() method as open. This allows it to be overridden.
Now, let’s extend the Item class and override the getIdOfItem() method:
12345 | class ItemWithCategory(id : String, name : String, val categoryId : String) : Item(id, name) { override fun getIdOfItem() : String { return id + name } } |
6. Conditional Statements
In Kotlin, conditional statement if is an equivalent of a function that returns some value. Let’s look at an example:
1234 | fun makeAnalyisOfCategory(catId : String) : Unit { val result = if (catId == "100" ) "Yes" else "No" println(result) } |
In this example, we see that if catId is equal to “100” conditional block returns “Yes” else it returns “No”. Returned value gets assigned to result.
You could create a normal if–else block:
123456 | val number = 2 if (number < 10 ) { println( "number less that 10" ) } else if (number > 10 ) { println( "number is greater that 10" ) } |
Kotlin has also a very useful when command that acts like an advanced switch statement:
12345 | val name = "John" when (name) { "John" -> println( "Hi man" ) "Alice" -> println( "Hi lady" ) } |
7. Collections
There are two types of collections in Kotlin: mutable and immutable. When we create immutable collection it means that is read only:
1 | val items = listOf( 1 , 2 , 3 , 4 ) |
There is no add function element on that list.
When we want to create a mutable list that could be altered, we need to use mutableListOf() method:
12 | val rwList = mutableListOf( 1 , 2 , 3 ) rwList.add( 5 ) |
A mutable list has add() method so we could append an element to it. There are also equivalent method to other types of collections: mutableMapOf(), mapOf(), setOf(), mutableSetOf()
8. Exceptions
Mechanism of exception handling is very similar to the one in Java.
All exception classes extend Throwable. The exception must have a message, stacktrace, and an optional cause. Every exception in Kotlin is unchecked, meaning that compiler does not force us to catch them.
To throw an exception object, we need to use the throw-expression:
1 | throw Exception( "msg" ) |
Handling of exception is done by using try…catch block(finally optional):
123456789 | try { } catch (e : SomeException) { } finally { } |
9. Lambdas
In Kotlin, we could define lambda functions and pass them as arguments to other functions.
Let’s see how to define a simple lambda:
1 | val sumLambda = { a : Int, b : Int -> a + b } |
We defined sumLambda function that takes two arguments of type Int as an argument and returns Int.
We could pass a lambda around:
1234567891011 | @ Test fun givenListOfNumber _ whenDoingOperationsUsingLambda _ shouldReturnProperResult() { // given val listOfNumbers = listOf( 1 , 2 , 3 ) // when val sum = listOfNumbers.reduce { a, b -> a + b } // then assertEquals( 6 , sum) } |
10. Looping Constructs
In Kotlin, looping through collections could be done by using a standard for..in construct:
1 | val numbers = arrayOf( "first" , "second" , "third" , "fourth" ) |
123 | for (n in numbers) { println(n) } |
If we want to iterate over a range of integers we could use a range construct:
123 | for (i in 2 .. 9 step 2 ) { println(i) } |
Note that the range in the example above is inclusive on both sides. The step parameter is optional and it is an equivalent to incrementing the counter twice in each iteration. The output will be following:
1234 | 2 4 6 8 |
We could use a rangeTo() function that is defined on Int class in the following way:
1 | 1 .rangeTo( 10 ).map{ it * 2 } |
The result will contain (note that rangeTo() is also inclusive):
1 | [ 2 , 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18 , 20 ] |
11. Null Safety
Let’s look at one of the key features of Kotlin – null safety, that is built into the language. To illustrate why this is useful, we will create simple service that returns an Item object:
123456 | class ItemService { fun findItemNameForId(id : String) : Item? { val itemId = UUID.randomUUID().toString() return Item(itemId, "name-$itemId" ); } } |
The important thing to notice is returned type of that method. It is an object followed by the question mark. It is a construct from Kotlin language, meaning that Item returned from that method could be null. We need to handle that case at compile time, deciding what we want to do with that object (it is more or less equivalent to Java 8 Optional<T> type).
If the method signature has type without question mark:
1 | fun findItemNameForId(id : String) : Item |
then calling code will not need to handle a null case because it is guaranteed by the compiler and Kotlin language, that returned object can not be null.
Otherwise, if there is a nullable object passed to a method, and that case is not handled, it will not compile.
Let’s write a test case for Kotlin type-safety:
1234567 | val id = "item_id" val itemService = ItemService() val result = itemService.findItemNameForId(id) assertNotNull(result?.let { it -> it.id }) assertNotNull(result!!.id) |
We are seeing here that after executing method findItemNameForId(), the returned type is of Kotlin Nullable. To access a field of that object (id), we need to handle that case at compile time. Method let() will execute only if a result is non-nullable. Id field can be accessed inside of a lambda function because it is null safe.
Another way to access that nullable object field is to use Kotlin operator !!. It is equivalent to:
1234 | if (result == null ){ throwNpe(); } return result; |
Kotlin will check if that object is a null if so, it will throw a NullPointerException, otherwise it will return a proper object. Function throwNpe() is a Kotlin internal function.
12. Data Classes
A very nice language construct that could be found in Kotlin is data classes (it is equivalent to “case class” from Scala language). The purpose of such classes is to only hold data. In our example we had an Item class that only holds the data:
1 | data class Item( val id : String, val name : String) |
The compiler will create for us methods hashCode(), equals(), and toString(). It is good practice to make data classes immutable, by using a val keyword. Data classes could have default field values:
1 | data class Item( val id : String, val name : String = "unknown_name" ) |
We see that name field has a default value “unknown_name”.
13. Extension Functions
Suppose that we have a class that is a part of 3rd party library, but we want to extend it with an additional method. Kotlin allows us to do this by using extension functions.
Let’s consider an example in which we have a list of elements and we want to take a random element from that list. We want to add a new function random() to 3rd party List class.
Here’s how it looks like in Kotlin:
1234 | fun <T> List<T>.random() : T? { if ( this .isEmpty()) return null return get(ThreadLocalRandom.current().nextInt(count())) } |
The most important thing to notice here is a signature of the method. The method is prefixed with a name of the class that we are adding this extra method to.
Inside the extension method, we operate on a scope of a list, therefore using this gave use access to list instance methods like isEmpty() or count(). Then we are able to call random() method on any list that is in that scope:
123 | fun <T> getRandomElementOfList(list : List<T>) : T? { return list.random() } |
We created a method that takes a list and then executes custom extension function random() that was previously defined. Let’s write a test case for our new function:
12345 | val elements = listOf( "a" , "b" , "c" ) val result = ListExtension().getRandomElementOfList(elements) assertTrue(elements.contains(result)) |
The possibility of defining functions that “extends” 3rd party classes is a very powerful feature and can make our code more concise and readable.
14. String Templates
A very nice feature of Kotlin language is a possibility to use templates for Strings. It is very useful because we do not need to concatenate Strings manually:
1234 | val firstName = "Tom" val secondName = "Mary" val concatOfNames = "$firstName + $secondName" val sum = "four: ${2 + 2}" |
We can also evaluate an expression inside the ${} block:
12 | val itemManager = ItemManager( "cat_id" , "db://connection" ) val result = "function result: ${itemManager.isFromSpecificCategory(" 1 ")}" |
15. Kotlin/Java Interoperability
Kotlin – Java interoperability is seamlessly easy. Let’s suppose that we have a Java class with a method that operates on String:
12345 | class StringUtils{ public static String toUpperCase(String name) { return name.toUpperCase(); } } |
Now we want to execute that code from our Kotlin class. We only need to import that class and we could execute java method from Kotlin without any problems:
12345 | val name = "tom" val res = StringUtils.toUpperCase(name) assertEquals(res, "TOM" ) |
As we see, we used java method from Kotlin code.
Calling Kotlin code from a Java is also very easy. Let’s define simple Kotlin function:
12345 | class MathematicsOperations { fun addTwoNumbers(a : Int, b : Int) : Int { return a + b } } |
Executing addTwoNumbers() from Java code is very easy:
123 | int res = new MathematicsOperations().addTwoNumbers( 2 , 4 ); assertEquals( 6 , res); |
We see that call to Kotlin code was transparent to us.
When we define a method in java that return type is a void, in Kotlin returned value will be of a Unit type.
There are some special identifiers in Java language ( is, object, in, ..) that when used them in Kotlin code needs to be escaped. For example, we could define a method that has a name object() but we need to remember to escape that name as this is a special identifier in java:
123 | fun ` object `() : String { return "this is object" } |
Then we could execute that method:
1 | ` object `() |
Conclusion
This article makes an introduction to Kotlin language and it’s key features. It starts by introducing simple concepts like loops, conditional statements, and defining classes. Then shows some more advanced features like extension functions and null safety.
The implementation of all these examples and code snippets can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.
KOREAN VERSION
두개의 파라미터를 받는 함수를 만들때는 아래와 같이 선언해보자.
1 | fun sum(a : Int, b : Int) : Int { return a + b } |
kotlin의 변수 종류에는 크게 두가지 종류가 있다. 이는 scala 와 동일한 원리 인데, val 과 var 가 있다. val은 value에서 나온 변수로 변경이 불가능한 변수(immutable)를 말한다. 반대로 var 는 vaiable 에서 나온 var 로 변경이 가능한 변수(mutable) 을 의미한다.
1 | val a : Int = 1 val b = 1 val c : Int c = 1 |
1 | var x = 5 x + = 1 |
4. Optional Fields
코틀린은 널이 가능하도록(java 에서 optional을 생각하면 좀 더 빠를것이다) 널값이 가능한 타입의 필드를 선언하기 위해서 우리는 타입뒤에 물음표(?) 를 붙여주어 선언한다.(이는 .net c# 4.7 이상의 문법과도 동일한 내용이다.)
1 | val email : String? |
널이 가능한 필드를 선언하고 그 필드에 널을 할당하기 위해서는 다음과 같이 선언하면 가능하다:
1 | val email : String? = null |
그런데 만약 아래와 같이 선언했다면..
1 | val email : String = "value" |
이것은 우리가 이메일이라는 필드에 값을 할당할 필요가 있다는 것이다. 이렇게 선언했을때는 “null” 값을 가질 수 없다. 이와 관련해서는 코틀린의 null safety 관련된 내용을 뒷부분에 다루려고 한다.
5. Classes
특정 카테고리의 상품을 위한 간단한 클래스를 만드는 예시를 들어보자. 만들려고 하는 ItemManager 클래스는 두개의 필드를 가진 기본 생성자가 존재해야한다 – categoryId 와 dbConnection 그리고 email 필드는 optional 이다.(null 값이 가능한 필드라는 뜻):
1234 | class ItemManager( val categoryId : String, val dbConnection : String) { var email = "" // ... } |
이 ItemManager(…) 클래스 생성자는 두개의 필드와 생성자로 생성되었다.
(주의 : 여기서 사용되는 생성자는 변수 생성시 val 키워드를 사용하여서 argument 를 생성하였다. 이 뜻은 해당 키워드에 맞는 필드가 final 이고 변하지 않을(immutable ) 일 것이라는 것을 의미한다. 만약 우리가 var 키워드를 썼다면 (이메일 필드를 선언한것과 같이) 이것은 변할수있는(mutable) 변수였을 것이다.) 변수였을 것이다.
자, 기본 생성자를 이용하여서 ItemManager 의 객체(instance) 를 생성해보자!
1 | ItemManager( "cat_id" , "db://connection" ) |
우리는 named parameter 를 사용해서 ItemManager 를 생성할 수 있다. 이것은 우리가 같은타입의 두개의 파라미터가 예시로 든다면.. 매우 유용하다.명명 된 매개 변수를 사용하여 ItemManager를 구성 할 수 있다. 이 예제 함수에서 같은 유형의 두 매개 변수를 사용하는 경우에 매우 유용하고, 문자열, 순서를 혼돈하게 하지 않는다. 이름 지정 매개 변수를 사용하여 지정된 매개 변수를 명시 적으로 작성할 수 있다. ItemManager 클래스에는 categoryId 및 dbConnection이라는 두 개의 필드가 있으므로 명명 된 매개 변수를 사용하여 둘 다 참조 할 수 있다.
1 | ItemManager(categoryId = "catId" , dbConnection = "db://Connection" ) |
이것은 함수에 더 많은 인수를 전달해야 할 때 매우 유용하다.
추가 생성자가 필요한 경우 constructor 키워드를 사용하여 정의 할 수 있다. 이메일을 설정하는 다른 생성자를 정의 해 보자!
1234 | constructor(categoryId : String, dbConnection : String, email : String) : this (categoryId, dbConnection) { this .email = email } |
이 생성자는 이메일 필드를 설정하기 전에 위에서 정의한 기본 생성자를 호출한다. 그리고 기본 생성자에서 val 키워드(immutable)를 사용하여 categoryId 및 dbConnection을 변경할 수 없도록 이미 정의 했으므로 추가 생성자에서 val 키워드를 반복 할 필요가 없다.
Now, let’s create an instance using the additional constructor:
1 | ItemManager( "cat_id" , "db://connection" , "foo@bar.com" ) |
만약 ItemManager 인스턴스 메소드를 정의하려면 fun 키워드를 사용해서 정의할 수 있다.
123 | fun isFromSpecificCategory(catId : String) : Boolean { return categoryId == catId } |
6. Inheritance
기본적으로 Kotlin의 클래스는 확장을 위해 닫힙니다. Java에서 final로 표시된 클래스와 같습니다.확장을 위해 클래스를 열도록 지정하려면 클래스를 정의 할 때 open 키워드를 사용합니다.By default, Kotlin’s classes are closed for extension — the equivalent of a class marked final in Java.
In order to specify that a class is open for extension, you would use the open keyword when defining the class.
Let’s define an Item class that is open for extension:
12345 | open class Item( val id : String, val name : String = "unknown_name" ) { open fun getIdOfItem() : String { return id } } |
Note that we also denoted the getIdOfItem() method as open. This allows it to be overridden.
Now, let’s extend the Item class and override the getIdOfItem() method:
12345 | class ItemWithCategory(id : String, name : String, val categoryId : String) : Item(id, name) { override fun getIdOfItem() : String { return id + name } } |
7. Conditional Statements
In Kotlin, conditional statement if is an equivalent of a function that returns some value. Let’s look at an example:
1234 | fun makeAnalyisOfCategory(catId : String) : Unit { val result = if (catId == "100" ) "Yes" else "No" println(result) } |
In this example, we see that if catId is equal to “100” conditional block returns “Yes” else it returns “No”. Returned value gets assigned to result.
You could create a normal if–else block:
123456 | val number = 2 if (number < 10 ) { println( "number less that 10" ) } else if (number > 10 ) { println( "number is greater that 10" ) } |
Kotlin has also a very useful when command that acts like an advanced switch statement:
12345 | val name = "John" when (name) { "John" -> println( "Hi man" ) "Alice" -> println( "Hi lady" ) } |
8. Collections
There are two types of collections in Kotlin: mutable and immutable. When we create immutable collection it means that is read only:
1 | val items = listOf( 1 , 2 , 3 , 4 ) |
There is no add function element on that list.
When we want to create a mutable list that could be altered, we need to use mutableListOf() method:
12 | val rwList = mutableListOf( 1 , 2 , 3 ) rwList.add( 5 ) |
A mutable list has add() method so we could append an element to it. There are also equivalent method to other types of collections: mutableMapOf(), mapOf(), setOf(), mutableSetOf()
9. Exceptions
Mechanism of exception handling is very similar to the one in Java.
All exception classes extend Throwable. The exception must have a message, stacktrace, and an optional cause. Every exception in Kotlin is unchecked, meaning that compiler does not force us to catch them.
To throw an exception object, we need to use the throw-expression:
1 | throw Exception( "msg" ) |
Handling of exception is done by using try…catch block(finally optional):
123456789 | try { } catch (e : SomeException) { } finally { } |
10. Lambdas
In Kotlin, we could define lambda functions and pass them as arguments to other functions.
Let’s see how to define a simple lambda:
1 | val sumLambda = { a : Int, b : Int -> a + b } |
We defined sumLambda function that takes two arguments of type Int as an argument and returns Int.
We could pass a lambda around:
1234567891011 | @ Test fun givenListOfNumber _ whenDoingOperationsUsingLambda _ shouldReturnProperResult() { // given val listOfNumbers = listOf( 1 , 2 , 3 ) // when val sum = listOfNumbers.reduce { a, b -> a + b } // then assertEquals( 6 , sum) } |
11. Looping Constructs
In Kotlin, looping through collections could be done by using a standard for..in construct:
1 | val numbers = arrayOf( "first" , "second" , "third" , "fourth" ) |
123 | for (n in numbers) { println(n) } |
If we want to iterate over a range of integers we could use a range construct:
123 | for (i in 2 .. 9 step 2 ) { println(i) } |
Note that the range in the example above is inclusive on both sides. The step parameter is optional and it is an equivalent to incrementing the counter twice in each iteration. The output will be following:
1234 | 2 4 6 8 |
We could use a rangeTo() function that is defined on Int class in the following way:
1 | 1 .rangeTo( 10 ).map{ it * 2 } |
The result will contain (note that rangeTo() is also inclusive):
1 | [ 2 , 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18 , 20 ] |
12. Null Safety
Let’s look at one of the key features of Kotlin – null safety, that is built into the language. To illustrate why this is useful, we will create simple service that returns an Item object:
123456 | class ItemService { fun findItemNameForId(id : String) : Item? { val itemId = UUID.randomUUID().toString() return Item(itemId, "name-$itemId" ); } } |
The important thing to notice is returned type of that method. It is an object followed by the question mark. It is a construct from Kotlin language, meaning that Item returned from that method could be null. We need to handle that case at compile time, deciding what we want to do with that object (it is more or less equivalent to Java 8 Optional<T> type).
If the method signature has type without question mark:
1 | fun findItemNameForId(id : String) : Item |
then calling code will not need to handle a null case because it is guaranteed by the compiler and Kotlin language, that returned object can not be null.
Otherwise, if there is a nullable object passed to a method, and that case is not handled, it will not compile.
Let’s write a test case for Kotlin type-safety:
1234567 | val id = "item_id" val itemService = ItemService() val result = itemService.findItemNameForId(id) assertNotNull(result?.let { it -> it.id }) assertNotNull(result!!.id) |
We are seeing here that after executing method findItemNameForId(), the returned type is of Kotlin Nullable. To access a field of that object (id), we need to handle that case at compile time. Method let() will execute only if a result is non-nullable. Id field can be accessed inside of a lambda function because it is null safe.
Another way to access that nullable object field is to use Kotlin operator !!. It is equivalent to:
1234 | if (result == null ){ throwNpe(); } return result; |
Kotlin will check if that object is a null if so, it will throw a NullPointerException, otherwise it will return a proper object. Function throwNpe() is a Kotlin internal function.
13. Data Classes
A very nice language construct that could be found in Kotlin is data classes (it is equivalent to “case class” from Scala language). The purpose of such classes is to only hold data. In our example we had an Item class that only holds the data:
1 | data class Item( val id : String, val name : String) |
The compiler will create for us methods hashCode(), equals(), and toString(). It is good practice to make data classes immutable, by using a val keyword. Data classes could have default field values:
1 | data class Item( val id : String, val name : String = "unknown_name" ) |
We see that name field has a default value “unknown_name”.
14. Extension Functions
Suppose that we have a class that is a part of 3rd party library, but we want to extend it with an additional method. Kotlin allows us to do this by using extension functions.
Let’s consider an example in which we have a list of elements and we want to take a random element from that list. We want to add a new function random() to 3rd party List class.
Here’s how it looks like in Kotlin:
1234 | fun <T> List<T>.random() : T? { if ( this .isEmpty()) return null return get(ThreadLocalRandom.current().nextInt(count())) } |
The most important thing to notice here is a signature of the method. The method is prefixed with a name of the class that we are adding this extra method to.
Inside the extension method, we operate on a scope of a list, therefore using this gave use access to list instance methods like isEmpty() or count(). Then we are able to call random() method on any list that is in that scope:
123 | fun <T> getRandomElementOfList(list : List<T>) : T? { return list.random() } |
We created a method that takes a list and then executes custom extension function random() that was previously defined. Let’s write a test case for our new function:
12345 | val elements = listOf( "a" , "b" , "c" ) val result = ListExtension().getRandomElementOfList(elements) assertTrue(elements.contains(result)) |
The possibility of defining functions that “extends” 3rd party classes is a very powerful feature and can make our code more concise and readable.
15. String Templates
A very nice feature of Kotlin language is a possibility to use templates for Strings. It is very useful because we do not need to concatenate Strings manually:
1234 | val firstName = "Tom" val secondName = "Mary" val concatOfNames = "$firstName + $secondName" val sum = "four: ${2 + 2}" |
We can also evaluate an expression inside the ${} block:
12 | val itemManager = ItemManager( "cat_id" , "db://connection" ) val result = "function result: ${itemManager.isFromSpecificCategory(" 1 ")}" |
16. Kotlin/Java Interoperability
Kotlin – Java interoperability is seamlessly easy. Let’s suppose that we have a Java class with a method that operates on String:
12345 | class StringUtils{ public static String toUpperCase(String name) { return name.toUpperCase(); } } |
Now we want to execute that code from our Kotlin class. We only need to import that class and we could execute java method from Kotlin without any problems:
12345 | val name = "tom" val res = StringUtils.toUpperCase(name) assertEquals(res, "TOM" ) |
As we see, we used java method from Kotlin code.
Calling Kotlin code from a Java is also very easy. Let’s define simple Kotlin function:
12345 | class MathematicsOperations { fun addTwoNumbers(a : Int, b : Int) : Int { return a + b } } |
Executing addTwoNumbers() from Java code is very easy:
123 | int res = new MathematicsOperations().addTwoNumbers( 2 , 4 ); assertEquals( 6 , res); |
We see that call to Kotlin code was transparent to us.
When we define a method in java that return type is a void, in Kotlin returned value will be of a Unit type.
There are some special identifiers in Java language ( is, object, in, ..) that when used them in Kotlin code needs to be escaped. For example, we could define a method that has a name object() but we need to remember to escape that name as this is a special identifier in java:
123 | fun ` object `() : String { return "this is object" } |
Then we could execute that method:
1 | ` object `() |
17. Conclusion
This article makes an introduction to Kotlin language and it’s key features. It starts by introducing simple concepts like loops, conditional statements, and defining classes. Then shows some more advanced features like extension functions and null safety.
The implementation of all these examples and code snippets can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.