July, 8 of 2019 Augusto Calado Bueno
Kotlin provides us two types of variables. One is var that can be resigned with other values. The other one is val, which, when assigned, will have always the same value.
A variable in Kotlin cannot be used until it has been assigned.
val people = HashMap<Int, KotlinPerson>()
people.put(1,KotlinPerson(1,"Augusto"))
Although the var people is assigned with a hashmap, it can still add or remove values from the hashMap, which still mutable. The people keyword that is immutable.
In Kotlin, there is a distinction between references that can hold null (nullable references) and those that can not (non-null references).
For example, a regular variable of type String can not hold null
“var name: String = null” It will not compile, because when we declare an object with explicit type, it cannot have the value null.
In Kotlin is allowed nulls when we declare a variable as nullable string, written String?
“var name: String? = null” -> nullable string. Take a look at the question mark "?"
When there is a null-able variable, if that (nullable variable) is followed by a question mark (?) then this now means if the name variable is null, we receive as result null, otherwise, call the method to uppercase.
var result = name?.toUpperCase()
A safe call can also be placed on the left side of an assignment. Then, if one of the receivers in the safe calls chain is null, the assignment is skipped, and the expression on the right is not evaluated at all:
// If either person
or person.department
is null, the function is not called:
person?.department?.head = managersPool.getManager()
All the objects in Kotlin have the let function, which executes a block ON our specific object.
Also, to perform a certain operation only for non-null values, you can use the safe call operator together with let.
var favoriteColor: String? = null
fun getLasLatter(a : String) = a.takeLast(1)
fun getLastLetterOfColor(): String {
return favoriteColor?.let {getLasLatter(it)} ?: ""
// favoriteColor is not null with a question mark, dot, (?.) then we'll run our let function and our lambda is going to be getLastLetter of it.
}
The examples bellow are problemes solved by using SAFE CALL with LET
return if (favoriteColor == null) "" getLasLatter(favoriteColor)
return getLasLatter(favoriteColor) ?: "" // this won't compile because favoriteColor is a nullable variable and getLastLetter can only be called with a non-null variable.
return getLasLatter(favoriteColor!!) ?: "" // will give us a NullPointerException
When we have a nullable reference r, we can say "if r is not null, use it, otherwise use some non-null value x"
Example: Java way verification
var favoriteColor: String? = null
fun getUpperCaseColor(): String {
return if (favoriteColor == null) "" else favoriteColor.toUpperCase()
}
Example: Kotlin way verification - Elvis Operator
Ex to solve WarningA:
fun getUpperCaseColor(): String {
return favoriteColor?.toUpperCase() ?: "" //
}
The not-null assertion operator (!!) converts any value to a non-null type and throws an exception if the value is null. We can write b!!, and this will return a non-null value of b (e.g., a String in our example) or throw an NPE if b is null
val result = name!!.toUpperCase() // can result a null point exception
If we set a variable without a specific type equals null ( var name = null), then that variable receives a special object which is called Nothing.
var example = null
The variable example is not a nullable string or a nullable integer, it is absolutely just an instance of nothing.
Strings are represented by the type String. Strings are immutable. Elements of a string are characters that can be accessed by the indexing operation: s[i].
A raw string is delimited by a triple quote ("""), contains no escaping and can contain newlines and any other characters:
val myString = """It is a test
|Don't do this in production environment""".trimMargin("|")
Use 3x (") to have a multiline string. We use the method trimMargin("|") to remove indentation caused by the extra spaces.
String literals may contain template expressions, i.e. pieces of code that are evaluated and whose results are concatenated into the string. A template expression starts with a dollar sign ($) and consists of either a simple name:
//Code next page
val i = 10
println("i = $i") // prints "i = 10"
or an arbitrary expression in curly braces:
val s = "abc"
println("$s.length is ${s.length}") // prints "abc.length is 3"
Kotlin double will be compiled to a Java literal double (the one which has the 'd' lowercase in java).
So, although there are no literals in Kotlin, so every double or integer is always an object, actually, it will be compiled into a literal to be run on the JVM.
var myDouble: Double = 17.1
The Java integer (the object that represents a int), in Kotlin is an Int with a capital I. It's not an Integer with a capital I.
var myInt: Int = 19
val myFloat: Float = 13.6f
val myLong: Long = 12L
In Kotlin if statements can be thinking as a function that returns a value
return if (myAnge > 20) true else false
An alternative of else if () statements is the when operator
Example - When Operator
fun getColorType(): String {
val color = getUpperCaseColor();
return when (color) {
"" -> empty // return if(color == "") empty
"RED", "GREEN", "BLUE" -> "RGB"
else -> "other"
}
}
All exceptions in Kotlin are UNCHECKED.
In Java, if you are calling a method which we know throws a particular type of exception, called a checked exception, then the compiler forces us to deal with that exception, either, handling it in a try-catch block or throwing it from the method we are working in.
Moreover, there is no WARN about whether an operation/method/logic will throw an exception (WE NEED TO BE CAREFUL WITH THIS SITUATION)
The important thing here is that when we put this annotation does not make any difference. In other words, it does not warn us about the possible exception might receive
So, why should I use this annotation? If there is some code in java using Throws, it can catch these exceptions.
@Throws (InterruptedException::class)
fun divide(a: Int, b:Int):Double {
//some code that throws an exception
}
In Kotlin, just like the if statement, the try statement is an expression. In other words, it can return a value.
Example - Assign the results of a try to a varible
var result = try {
doSomething()
}
catch (e: Exception) {
println(e)
}
This kind of try was introduced in java 7 and is used when there is an event that crashes, inside the codeblock of that try, any object that has been declared will be closed automatically.
The Kotlin version is de codeblocks defined with use statements, whose action is exactly the same as the try with resources.
val input = FileInputStream(“file.txt”)
input.use {
//An exception could be thrown here
//If that happens, so the object will be closed automatically
}
myVar is Double //Kotling Casting
SmartCasting: Means that if we have checked the data type with 'if', then within the code block of the statement, or variable will be automatically cast into the data type that we have checked for
Also, smartcasting will happens if the do a "null safe check"
Exemplo - SmartingCasting Kotlin
SmartCasting: if(result is BigDecimal) {
result.add(BigDecimal(43))
// Here we already can access the method of BigDecimal after the automatic casting
}
When there is NO EXPLICIT TYPE CHECK we explicitly use the keyword: 'as'
var myString = result as String
Loops Examples
[Basic for loop]
for(variable: Type in SomeCollection) { //implementation}
for(variable in SomeCollection) { //implementation}
for((id, title, firstName) in people) {// implementation} //Destructuring
for( i in 0..9) {//implementation} //this sintaxe is called range (number A to number B)
(0..9).forEach{ i -> println(i)}
[range in Kotlin]
(0 downTo 9).forEach{}
(0 until 9).forEach{}
(0..9 step 2).forEach{}
To declare a function in kotlin we need to use the keyword fun. The parameter besides the name, we also need to declare their types.
fun someMethod(value: Type): ReturnType {}
In Kotlin, the equivalent of a void function RETURNS A 'Unit' Object. We don't need explicit return that.
fun someFunc() {}
// It is a void function that returns Unit. Unit type declaration is opticonal
When a function has just a single expression or one line, the curly braces can be removed and the function declaration receives using the equal signal (=), the expression.
fun addTwoNumbers(one: Double, two: Double): Double = one + two
We can, when calling a function, change the order of the parameters by using their names and assigning them their respective values.
fun printSomeMaths(one: Double, two: Double) = //SomeCode
//calling the printSomeMaths
printSomeMaths(two=2.1, one=1.1)
Optional Parameters came to provide a better way to improve the Overloaded functions from java. In order to do that we need to define default parameters in the method body.
fun printSomeMaths(one: Double = 1.1, two: Double = 2.1) = //some code
//calling the printSomeMaths
printSomeMaths(two = 5.6)
Since we set default values for the parameters one and two, I don't need to specify the value of both.
Are the parameters of a function val or vars?
All of the parameters in a function name are effectively vals rather than vars. In other words, they are immutable.
A parameter of a function (normally the last one) may be marked with vararg modifier to to have a variable number of arguments.
fun asList(vararg ts: T): List {//some code}
Top-level-functions are functions that aren't defined within a class, In addition to top level functions, Kotlin functions can also be declared local, as member functions and extension functions.
Example - Member Functions
fun dfs(graph: Graph) {
val visited = HashSet()
fun dfs(current: Vertex) { // Member Functions
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(graph.vertices[0])
}
Java allows us to pass functions as parameter to others function:
public void methodTakesALambda(String input, Function<String, Integer> action)
// This method receives an String input and a function that will convert the string into a int
Kotlin Way to pass lambda as Parameter
fun methodTakesALambda (input: String, action: (String) -> Int) {//somecode}
Example - Kotlin Way to pass lambda as Parameter
fun foo(bar: Int = 0, baz: Int = 1, qux: () -> Unit) { ... }
foo(1) { println("hello") } // Uses the default value baz = 1
foo(qux = { println("hello") }) // Uses both default values bar = 0 and baz = 1
foo { println("hello") } // Uses both default values bar = 0 and baz = 1
Unlike Java, where every class extends from the superclass Object. In Kotlin, the root of the Kotlin class hierarchy is Any. In other words, Every Kotlin class has Any as a superclass.
Now in Kotlin instead of 'Object' the data type is called 'Any' with a capital 'A'. That the Kotlin equivalent to Java's 'object'.
Object result; // Java code
var result: Any // Kotlin code
All Classes in Kotlin are final. We cannot extend it unless we use the open keyword in order to make a class extendable.
If a class is declared as private so that just can be visible within the package that it was declared
It is possible to define the class and provide parameters immediately after the class name in round brackets. This part is referred as default constructor
class Customer (val name: String, val address: String, var age: Int) { //Implementation}
This is a shorthand way of saying there is a class with these as internal fields that they're publicly visible, but we also now get a constructor, which sets each of these variables.
Besides the default constructor we have secondary constructors. They effectively are functions. And as were shown before, all function has its parameters categorized as val, in other words, is not allowed to change its value.
To exemplify lets imagen that we want to create a customer who is going to have an empty address.
class Customer (val name: String, val address: String, var age: Int) {
constructor(name: String, age: Int) : this(name, “”, age) //secondary constructor
}
There is a restriction in secondary constructors, which is the obligation to call the primary constructor.
How to put some code to run in the code block of the primary constructor? The way to do this is using a block that starts with the keyword init.
Example: Primary and Secondary Constructor
class Customer (val name: String, val address: String, var age: Int) {
constructor(name: String, age: Int) : this(name, “”, age) {
//secondary constructor
println(“Hellow here is the secondary constructor”)
}
init {
println(“Hellow here is the init from primary constructor”)
}
}
Important: When there are codes in the secondary constructor and in the init block. If an object was created by using the secondary constructor. First, all the code implemented in the init block runs and, only after that, the code declared in the secondary constructor will run.
val customer = Customer(“John”,31)
//Output
Hellow here is the init from primary constructor
Hellow here is the secondary constructor
Alternative ways to class design
class Customer (val name: String, var age: Int) {
val address: String
constructor(name: String, address: String, age: Int) : this(name, age) {
//secondary constructor
this.adress = address
}
init {
address = “”
}
}
class Customer (val name: String, val address: String = “”, var age: Int) { }
The getters and setters methods are not explicit in the body class in Kotlin. However, they are still there. If for some reason there is the necessity to override one of them so immediately after the variable declaration, we then are going to write a method called set.
Example - Override Set Method
class MyCustomer (val name: String, var age: Int) {
var approved: Boolean = false;
set( value) {
if( age >= 21 ) {
field = value //the keyword 'field' refers to approved variable
}
else {
println("cannot be approved")
}
}
val nextAge
get() = age + 1
}
Although we use the keyword set and get, we still reference the own field name (approved in or case) to perform those operations.
val customer = MyCustomer(“John”, 31)
customer.approved = true //set method beeing used
The kotlin way to create static functions requires a special block of code which starts with the keywords “companion object”. Everything inside that block is going to be a static function.
Rather than Java, in kotlin to override a function why don't use the annotation @override. We just need to start the function that we intend to override with the keyword override
An alternative way to create a class, mainly when the goal of that class is to store data. Besides de standards getters and setters method we, also, got the toString, hashcode and equals method
Any variable defined in the primary constructor will be used by the hash and equal methods
data class Customer (var name:String){}
val customer5 = customer4.copy(name="newName")
This is a shortcut way of creating multiple variables and assigning them to the fields within an object, and the ability to do that is called destructuring.
val (name, address, age) = customer5
If a class has a companion object defined, you can also define extension functions and properties for the companion object:
class MyClass {
companion object { } // will be called "Companion"
}
fun MyClass.Companion.foo() { ... }
Just like regular members of the companion object, they can be called using only the class name as the qualifier.
In Kotlin interfaces can be there function definitions, variable declarations and even function implementations
Implementation
So this colon-syntax-- after the class name is, if what follows is an interface than we are implementing the interface-- if what follows is an class, then we are extending the class. There is one other difference though and that is if we are extending a class, we must call its constructor passing in any parameters that the class we're extending need.
In Kotlin, to create a custom exception, what we need to do is to create a class that extends the class called Throwable.
method listOf("Red","Blue"), provide us a immutable list in Kotlin
method mutableListOf(), provided by Kotlin allows us to instanctiate an mutable list
val months = mutableSetOf("Jab","Feb")
val webColors = mapOf("red" to "ff0000", "blue" to "00ff00")
// here the type of each key and value is inferred
In Kotlin array is a class, and it acts very much like a collection. All arrays in Kotlin are mutable. There is no immutable array such as sets and map
val intArray : IntArray = intArrayOf(1,2,3,4,5)
intArray.set(3,-4)
intArray[3] = -7
How to compare reference and value equality in Kotlin
Reference equality object1 === object2
Value equality object1 == object2
Kotlin allows us to add functions to existing classes. Any classes, even final classes, that has not been opened and it is possible to add additional functions into that class.
The extension function is the functions that were added in some existing class. If I create a new method for String class means that the entire package which we are working in, strings now have an additional function,
Example - Extension Functions - String class
fun String.myNiceName(): String { //Do Something….}
In Kotlin, when we want to provide a lambda expression to a function and it is the last parameter. It can be provided outside the round brackets ()
fun applyFunctionString( input:String, myFunction: (String) -> String) : String {
return myFunction(input);
}
//main
var myString = applyFunctionString("augusto"){x-> x.toSentenseCase()} //
var myString = applyFunctionString("augusto"){it.toSentenseCase()} //
Keyword "it" refers to the default parameter if it's a single parameter in lambda
fun toSentenseCase(input: String) : String {
return input[0].toUpperCase() + input.substring(1)
}
//main
var myString = applyFunctionString("augusto", ::toSentenseCase)
//:: is called reflection sintaxe
The above code shows us that in kotlin we can refer a function and pass it as a parameter to another function.
[MAP] collection method map returns a list containing the results of applying the given transform function to each element in the original array.
val numbers = listOf(1, 2, 3)
println(numbers.map { it * it }) // [1, 4, 9]
[Filter] A collection method Filter returns a list containing the results of applying the given transform function to each element in the original array.
// It generates an one to one mapping
val originalMap = mapOf("ç0" to 0, "key2" to 2, "key3" to 3)
val filteredMap = originalMap.filter { it.value < 2 }
[FlatMap] A collection method FlatMap returns a single list of all elements yielded from results of transform function being invoked on each element of original array.
colorCollection.flatMap{if (it.startsWith("b")) listOf (it,it) else listOf(it)}.forEach{println(it)}
[Reduce] A collection method reduce will convert a collection to a single value.
colorCollection.reduce(result,value -> result + value + ",")
//there are loops that happens and every time the loop happens, result becomes a result plus value plus a comma.
[Fold] An collection methood Fold accumulates value starting with initial value and applying operation from left to right to current accumulator value and each element.
colorCollection.fold(0){result,value -> result + value + ",")}
//fold(initial value){...}
If we declare a dependency injection like we tend to do in Java we get a error that says “Property must be initialized or be abstract”.
@Autowired
val theater: TheaterService
There is an extra keyword we need to use in Kotlin when we are Autowiring in an object when we use a dependency injection to create the instance of an object into our class. And that additional keyword is lateinit.
@Autowired
lateinit var theater: TheaterService
When lateinit is added in the variable autowired, it is saying that that object is going to be initialised sometime very soon after the controller it is on. Also, it tells to Kotlin there is some other code somewhere that is responsible for doing that initialization.
Observation - lateinit only works with var and is used to create dependencies injection
The way that Spring will create backing beans, first Spring instantiates a new Backing beans and it is going to use the set methods to bound the values from the original Backing Beans to it newest bean. In view of that, is import determine which variable will be var or val, because when Spring is filling the new object backing bean it will not be able to set up the val variables.
@PostMapping("checkAvailability")
fun checkAvailability(bean: CheckAvailability): ModelAndView {
//Do Something
return ModelAndView("seatBooking", "bean", bean)
}
When hibernate loads data from a database, it instantiates the object using the default constructor, and then sets each of our properties using setter methods. It can be a problem is there is no default constructor, set methods for the variables and variabels declared as val.
To solve that problem, the Kotlin developers have released compiler plugins. One of them is allow JPA instantiate entity objects with a default constructors and then call setters, however it does not allow any other code to do that.
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
The methods findBySomething perhaps return an object, however is possible anything match with the criteria, thus the return will be null. But in kotlin it is not the case, if nothing is found so our return is an Optional.