Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Kotlin for Enterprise Applications using Java EE

You're reading from   Kotlin for Enterprise Applications using Java EE Develop, test, and troubleshoot enterprise applications and microservices with Kotlin and Java EE

Arrow left icon
Product type Paperback
Published in Nov 2018
Publisher Packt
ISBN-13 9781788997270
Length 388 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Raghavendra Rao K Raghavendra Rao K
Author Profile Icon Raghavendra Rao K
Raghavendra Rao K
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Kotlin – A First look FREE CHAPTER 2. Kotlin – The Game Changer 3. An Overview of Java EE and Kotlin 4. Kotlin with JSF and CDI 5. Kotlin with JPA and EJB 6. Enterprise Messaging with Kotlin 7. Developing RESTful Services with JAX-RS 8. Securing JAVA EE Applications with Kotlin 9. Implementing Microservices with Kotlin 10. Performance Monitoring and Logging 11. Design Patterns with Kotlin 12. Other Books You May Enjoy

A quick tour of Kotlin

In this section, let's take a quick look at the world of Kotlin and understand the basics of the language.

Declaring variables

To declare a variable in Kotlin, we use either the val or var keywords.

Let's take a look at the following example:

val number = 10
println(number)

The preceding code prints the value that is assigned to a variable number, as can be seen in the following screenshot:

However, if we try to change the value assigned to the number, we will get a compilation error. Take a look at the following code:

val number = 10
println(number)
number = 11
println(number)

The output is as follows:

This is because val is immutable in Kotlin. This means that once a value is assigned, it cannot be modified. The keyword var, however, can be used to create a mutable variable.

Now consider the code for 3_valnVar.kts:

val number = 10
println(number)

var anotherNumber = 15
println(anotherNumber)

anotherNumber = 20
println(anotherNumber)

The output for the preceding code is as follows:

Let's examine what happens if we assign a string value to the preceding code:

val number = 10
println(number)

var anotherNumber = 15
println(anotherNumber)

anotherNumber = "20"
println(anotherNumber)

Compile this and see what happens:

In this case, the compiler throws an error saying that the type is mismatched. This is because the anotherNumber variable was inferred to be of type int and, as a result, we cannot assign a different type.

The type is inferred from the context and type safety is also guaranteed.

Both var and val are used to declare variables in Kotlin. val creates immutable variables, and var creates mutable variables. Variables declared using the val keyword cannot be changed once a value is assigned to them. This is similar to a final variable in Java. val is used to create constants. Variables declared using the var keyword can be changed later in the program. This corresponds to a regular Java variable.

Data types in Kotlin

The basic data types in Kotlin are numbers, characters, Boolean, arrays, and strings. These are described as follows:

  • Number: Kotlin provides the following built-in types that represent numbers, similar to Java:
Type Byte
Double 8
Float 4
Long 8
Int 4
Short 2
Byte 1
Unlike in Java, characters are not numbers in Kotlin.
    • Byte: Byte is a one-byte or 8-bit type for which values range from -128 to 127
    • Short: Short is a two-byte or 16-bit type for which values range from -32768 to 32767
    • Int: Int is a four-byte or 32-bit type for which values range from -2^31 to 2^31-1
    • Long: Long is an eight-byte or 64-bit type that can have values from -2^63 to 2^63-1
    • Double: Double is a double-precision, 8-byte, or 64-bit floating point
    • Float: Float is a single-precision, 4-byte or 32-bit floating point
    • Char: Char is used to represent a character in Kotlin, two-byte or 16-bit long, and is used to represent unicode characters

Char is a type used to represent characters. This is represented as follows:

val ch = 'a'
  • Boolean: Kotlin has a Boolean type to represent Booleans. It takes either true or false:
val isValid = true
val flag = false
If you are using IntelliJ IDEA, you can place your cursor inside the variable and press Ctrl + Shift + P to see its type.

Warnings

If we have created a variable and not used it, or if we forget to initialize a variable, we will get warnings, which can be useful to prevent errors. Kotlin provides many such warnings during compile time to avoid possible runtime errors. This increases program reliability.

Type inference

Kotlin is a statically-typed language. It executes the type inference for us, so we don't have to specify types. Consider the code for 3a_TypeInference.kts:

val message = "Hi there"
println(message)

When we run this code, we get the following output:

We can also explicitly specify the type. Consider the following code:

val message : String = "Hi there"
println(message)

This is an example of creating a variable of the String type and printing it in the console. This looks like Scala syntax and it is quite different from what we do in Java.

In Java, we would write this as follows:

String message = "Hi there";

Languages such as Kotlin emphasize that the name of a variable is more important than the nature of the variable. Kotlin puts the name of a variable or constant first and the type after in the var declaration syntax. The type is, in fact, optional; we don't have to specify it. We might think that this is the same as the dynamic type, where the type is resolved during runtime. Kotlin, however, actually infers the type at compile time.

Kotlin uses the String class from the JDK library. We can query the class that it uses as follows:

val message = "Hi there"
println(message.javaClass)

The output is as follows:

As a general rule, it's a good idea to specify type information when we write public-facing interfaces and, when using local variables, we can let the language infer the type.

String templates

Kotlin has support for templates for the String type. This is useful because it helps us to avoid concatenation in code.

Let's look at an example for 4_StringTemplate.kts:

val name = "Atrium"
println("Hello ${name}")

The output is as follows:

The curly brackets are optional here. println("Hello ${name}") can be written as println("Hello $name"), but it is good practice to use them to indicate the boundaries of the expression.

Let's look at 4a_StringTemplate.kts:

val name = "Atrium"
println("Hello $name")

The output is as follows:

Now consider the following code in Java:

myName= "tanbul"
System.out.println("my name is" + myName);

Here, we meant to print tanbul, but due to a formatting error, this code prints my name istanbul. We want to correct the code as follows:

myName= "tanbul"
System.out.println("my name is " + myName);

Kotlin's string template really helps to avoid any possible formatting errors from string concatenation. In Kotlin, we write the preceding code with clear syntax as follows:

myName= "tanbul"
println("my name is ${myName}")

This prints the following:

my name is tanbul

Multi-line String Literals

We can also define multiline string literals without having to use + + for concatenation. Let's create a multiline string in the example 4b_MultilineString.kts:

val name = "Atrium"
val message = """This is an example of
multiline String $name
"""
println(message)

The output is as follows:

Observe the indentation in the preceding example. If we don't want to use indentation, we can put | and use the trimMargin() function to trim the margin as follows:

val name = "Atrium"
val message = """This is an example of
|multiline String $name
"""
println(message.trimMargin())

The output is as follows:

One more thing that we can do is customize the character that we are going to use for margin separation and pass it to the trimMargin() function as follows:

val name = "Atrium"
val message = """This is an example of
^multiline String $name
"""
println(message.trimMargin("^"))

This gives the following output:

Expressions over statements

A statement is an element of a program that represents an action. An expression is a part of the statement that gets evaluated to a value. Statements introduce mutability. The more statements a program contains, the more mutability it will have. Mutability in code increases the chance that it is erroneous. Expressions, on the other hand, do not produce mutability. In purely functional constructs, there are no statements, there are only expressions. More expressions in a program mean less mutability and code that is more concise.

Consider the code for 5_Expressions.kts:

val number = 5
val evenOrOdd = if(number % 2 == 0) "is even number" else "is odd number"
println("$number $evenOrOdd")

The output is as follows:

In this case, the code has expressions and we are not mutating any state. We are simply assigning the result of an expression to a variable and printing it.

Similarly, try...catch is also an expression. try...catch is used for handling the exceptions. The last statement in the try block becomes the expression for the try block.

Functions

The fun keyword is used to define functions in Kotlin. In this section, we'll discuss writing a function, type inference, and specifying the return type.

In the following example, we create a function that takes two arguments of the String type and prints them to the console.

Consider the following code for 6_Function.kts:

fun welcome(name:String, msg:String) {
println("$msg $name")
}
welcome("Bob", "hello")

Note that we used string template to print the values without concatenating string literals.

The output of the preceding code as follows:

This is a simple example of creating a function and invoking it. Now, let's create a function that returns a value.

Consider the code for 6a_Function.kts:

fun welcome(name: String, msg:String) =
"$msg $name"
println(welcome("Bob", "hello"))

The output is as follows:

The = symbol says that Kotlin should infer the type from the context and identify the return type of the function. This is more suitable for single-line functions. For complex functions, we can specify the return type, in this case a String, and the return statement in the function, as shown here.

Consider the code for 6b_Function.kts:

fun welcome(name: String, msg:String) : String {
return "$msg $name"
}
println(welcome("Bob", "hello"))

The output is as follows:

We specified the String return type after the arguments prefixed by the colon (:) and the return statement in the function body.

Let's take a look at a function that doesn't return anything. We are interested in writing the void function.

Consider the code for 6c_Function.kts:

fun addNumbers(x: Int, y:Int) : Unit {
println("Sum of numbers is ${x+y}")
}
addNumbers(2, 3)

The output is as follows:

Unit is the representation of void in Kotlin. We can also write a void function without the Unit, as shown here.

Consider the code for 6d_Function:

fun addNumbers(x: Int, y:Int) {
println("Sum of numbers is ${x+y}")
}
addNumbers(2, 3)

The output is as follows:

Default arguments

Default values to the arguments is a nice way for an API to evolve. If we already have a function with two arguments and we need a third argument, we can make the transition easy by making use of default values to arguments. The existing code still works and, at the same time, we can invoke a function with new arguments. Default values to arguments make the API simple and expressive.

Let's write an example to provide a default value to an argument to the function.

Consider the code for 6e_DefaultArguments.kts:

fun welcome(name: String, msg:String = "Hey") {
println("$msg $name")
}
welcome("Bob", "hello")
welcome("Berry")

In this case, we are not providing the second argument, msg, to the welcome() function. It therefore uses a default message instead. The output is as follows:

As shown in the preceding example, a default value to an argument can be specified with an = symbol in the argument declaration. In this case, if we don't pass the second argument while invoking the function, a default value will be provided.

Named arguments

We can use a named argument to invoke a function and pass the arguments by naming them. Since the arguments are named, the order doesn't need to be maintained.

Let's take a look at the following example. Consider the code for 6f_NamedArguments.kts:

fun welcome(name: String, msg:String = "Hey") {
println("$msg $name")
}
welcome(msg = "How are you", name = "Mark")

The output is as follows:

We can also mix named arguments with arguments without a name. Consider the code for 6g_NamedArguments.kts:

fun welcome(name: String, msg:String = "Hey") {
println("$msg $name")
}
welcome("Joseph", msg = "Hi")

The output is as follows:

In this case, we invoked the function with one positional argument with the value Joseph and one argument named msg. When we are dealing with multiple arguments in a method, we can choose to send some arguments positionally by maintaining the order and to send others as named arguments. The language gives us the flexibility to invoke functions with type safety.

varargs and spread

Kotlin supports a variable number of arguments. vararg is the keyword that is used for a variable number of arguments. Using vararg, we can pass multiple arguments to a function.

Let's see an example of 7a_VarArgs.kts:

fun findMaxNumber(vararg numbers : Int) =
numbers.reduce { max , e -> if(max > e) max else e }

println(findMaxNumber(1,2,3,4))

The output is as follows:

The findMaxNumber() function takes a variable number of arguments and finds the maximum. We invoked reduce on the numbers and wrote a lambda expression. In Kotlin, a lambda is specified using curly braces.

If we have an array of numbers, we can invoke the findMaxNumber() function using the spread operator without changing the function argument type.

Consider the following code for 7b_Spread.kts:

fun findMaxNumber(vararg numbers : Int) =
numbers.reduce { max , e -> if(max > e) max else e }

val numberArray = intArrayOf(25,50,75,100)
println(findMaxNumber(*numberArray))

The output is as follows:

Note that * acts as a spread operator here. We can combine this with discrete values, as shown in the following 7c_Spread file.

Consider the code for 7c_Spread.kts:

fun findMaxNumber(vararg numbers : Int) =
numbers.reduce { max , e -> if(max > e) max else e }

val numberArray = intArrayOf(25,50,75,100)
println(findMaxNumber(4,5,*numberArray,200, 10))

The output is as follows:

The for loop

Let's write a for loop to print numbers. Consider the code for 8a_ForLoop.kts:

for(num in 1 .. 5){
println(num)
}

The output is as follows:

.. is used to specify the range, meaning the program prints numbers from 1 to 5 inclusively.

To exclude a range, the until keyword is used. Consider the code for 8b_ForLoop_Until.kts:

for(num in 1 until 5){
println(num)
}

The output is as follows:

If we want our numbers to be inclusive, we use (..). If we want to exclude the range, we use until.

If we want to traverse the range in a given step size, we can use step. Consider the code for 8c_ForLoop_Step.kts:

for(num in 1 .. 10 step 2){
println(num)
}

The output is as follows:

If we want to iterate in reverse order, we can use downTo.

Consider the code for 8d_ForLoop_downTo.kts:

for(num in 25 downTo 20){
println(num)
}

This gives us the following output:

If we want to iterate in reverse order in a given step size, we can use downTo and step.

Consider the code for 8e_ForLoop_downTo_Step.kts:

for(num in 25 downTo 15 step 2){
println(num)
}

The output is as follows:

.. works on a range in ascending order.

Now, consider the code for 8e1_ForLoop_downTo.kts:

for(num in 25 .. 20){
println(num)
}

The output is as follows:

This code compiles without any errors, but when you run it, there will be no output.

For downTo and step, the value has to be a positive number. If we give a negative number, such as -2, it will produce a compilation error.

Consider the code for 8e2_ForLoop_downTo_Step.kts:

for(num in 25 downTo 15 step -2){
println(num)
}

The output is as follows:

Iterating over a list

Collections are used for storing and processing a set of objects. Kotlin provides an elegant syntax for iteration over a collection.

Consider the code for 9_IteratingOverList.kts:

val names = listOf("Mark", "Tina", "Williams")
for(name in names) {
println(name)
}

The output is as follows:

If we are interested in getting the index value, we can do that by running the indices command on the collection.

Consider the code for 9a_IteratingOverListIndex.kts:

val names = listOf("Mark", "Tina", "Williams")
for(index in names.indices) {
println(index)
}

The output is as follows:

As the index is a String, we can write an expression to print it. Consider the code for 9b_IteratingOverListIndex.kts:

val names = listOf("Mark", "Tina", "Joseph")
for(index in names.indices) {
println("$index")
}

The output is as follows:

We can also add names to the expression to get items out of the collection, as in the following example, in which we are printing index and name. Consider the code for 9c_IteratingOverList.kts:

val names = listOf("Mark", "Tina", "Joseph")
for(index in names.indices) {
println("$index: ${names.get(index)}")
}

The output is as follows:

When clause

when is a pattern matching clause in Kotlin. This helps us to write elegant code when dealing with pattern matching, meaning we can avoid using a lot of if else statements.

Let's write a function that takes an input, the type of which is not known. Any is used to indicate this and a when block is used to handle different patterns.

Consider the code for 10_when.kts:

fun processInput(input: Any) {
when(input) {
10 -> println("It is 10")
98,99 -> println("It is 98 or 99")
in 101 .. 120 -> println("More than 100")
is String -> println("This is ${input} of length ${input.length}")
else -> println("Not known")
}
}
processInput(10)
processInput(98)
processInput(99)
processInput(102)
processInput("hey there")
processInput(Thread())

The output is as follows:

Alongside the pattern matching, we can also perform type checking using the when block:

is String -> println("This is ${input} of length ${input.length}")

Note that the argument input is of the Any type. After type checking, the input is automatically cast to String, and we can use the length property, which is defined in the String class. The Kotlin language does the auto-typecasting for us, so we don't have to do any explicit type casting.

The nullable type

In Kotlin, we can declare a variable to hold a null value using the nullable type.

Let's write a program to check whether the given input is Web. If it is, it should return Web Development. If not, it should return an empty string.

Consider the code for 11_NullType.kts:

fun checkInput (data: String) : String {
if(data == "Web")
return "Web development"
return ""
}
println(checkInput ("Web"))
println(checkInput ("Android"))

The output is as follows:

So far, so good. Let's say that we want to return null if there is no match for the given input:

fun checkInput (data: String) : String {
if(data == "Web")
return "Web development"
return null
}
println(checkInput ("Web"))
println(checkInput ("Android"))

Let's compile and run this code:

Here, we get a compilation error because the return type is non-null by default in the function declaration. If we want to return null, we have to specify the type as nullable using ?:

fun checkInput (data: String) : String? {
if(data == "Web")
return "Web development"
return null
}
println(checkInput ("Web"))
println(checkInput ("Android"))

The output is as follows:

This code has compiled and run successfully. When we invoke the checkInput function with Web, it prints Web development. When we pass Android, it prints null to the console.

Similarly, when we receive data in response, we can also receive a nullable type from the function and perform a null check. Kotlin provides a very elegant syntax to do null checks.

Consider the code for 11a_NullCheck.kts:

fun checkInput (data: String) : String? {
if(data == "Web")
return "Web development"
return null
}
var response = checkInput ("Web")
println(response)
response ?.let {
println("got non null value")
} ?: run {
println("got null")
}

The checkInput() function returns a string if there is a match for Web, otherwise it returns null. Once the function returns a value, we can check whether it is null or non-null and act appropriately. ?.let and ?:run are used for this kind of scenario.

The output is as follows:

Consider the code for 11a_NullCheck.kts:

fun checkInput (data: String) : String? {
if(data == "Web")
return "Web development"
return null
}
var response = checkInput ("iOS")
println(response)
response ?.let {
println("got non null value")
} ?: run {
println("got null")
}

We now pass iOS instead of Web.

In this case, the output is as follows:

You have been reading a chapter from
Kotlin for Enterprise Applications using Java EE
Published in: Nov 2018
Publisher: Packt
ISBN-13: 9781788997270
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image