- Kotlin Function
- Variable Declaration
- Difference between var and val
- Array
- Type Conversion
- Kotlin Standard Input / Output
- Kotlin Control Flow
- Null Safety
- Safe Call Operator ( ?. )
- Elvis operator ( ?: )
- Not-null assertion operator ( !! )
- Functional Programming
- Lambda Expressions
//Syntax
fun function_name (argument : argument_type) : return_type {}
//Example
fun main(args: Array<String>) : Unit {
println("Hello World!")
}
Note: the unit return type is optional
fun main(args: Array<String>){
println("Hello World!")
}
A variable in Kotlin can be declared using var or val.
var string_variable = "Kotlin"
val int_variable = 12345
Here the variable string_variable is type string and int_variable is type integer, but we do not require to explicitly specify the type. Kotlin complier knows this by initilizer expression.
We can also specify the type if we want
var string_variable : String = "Kotlin"
val int_variable : Int = 12345
When we are just declaring a variable without initializing then we need to specify a type.
var a // [ERROR : No type, no body]
val b // [ERROR : No type, no body]
var a : String // :white_check_mark: OK
val b : Int // :white_check_mark: OK
- var (Mutable Variables): Can be changed inside the program at a later point in time.
- val (Immutable Variables): Once assigned cannot be changed at any point in time.
var name1 = "Rahul"
name1 = "Pandey" // :white_check_mark: OK
val name2 = "Rahul"
name2 = "Pandey" // [ERROR : Val cannot be reassigned]
Arrays in Kotlin can be created using library funcitons arrayOf() and Array()
// arrayOf() to create an array
val id = arrayOf(1,2,3,4,5,6,7,8,9)
val firstElement = id[0] // 1
val lastElement = id[id.size-1] // 9
// Array() to create an array
val arr = Array(5, { i -> i*2 }) // arr[0,2,4,6,8]
// first arg -> size of the array
// second arg -> used to initialize and return the value of array element given its index.
In Kotlin implicit type conversion is not allowed (as it is supported in Java)
// in Java
int value1 = 10;
long value2 = value1; // :white_check_mark: Valid in java
// in Kotlin
val value1 : Int = 10
val value2 : Long = value1 // [ERROR: Type mismatch.]
In Kotlin Type Conversion can be done explicitly where, small data type can be converted to large data type and vice versa. The list of helper functions used for numeric conversion in Kotlin is given below:
- toByte()
- toShort()
- toInt()
- toLong()
- toFloat()
- toDouble()
- toChar()
Kotlin output operation is performed using the standard methods print() and println().
println("Hello World!") // print the value inside () and move to the next line.
print("Welcome to this world") // print the value inside () and stay on the same line
We can use readLine() to read line of string input from standard input stream. It returns the line read or null.
println("Enter your name : ")
val name = readLine();
While using the readLine() function, we need to explicitly convert input lines other than String into their corresponding types.
println("Enter your age : ")
val age : Int = Integer.valueOf(readLine())
To input other data type rather than String, we need to use Scanner object of java.util.Scanner class from Java standard library.
import java.util.Scanner
fun main(args: Array<String>) {
val read = Scanner(System.`in`)
println("Enter your age")
var age = read.nextInt()
println("Your input age is "+age)
}
Here nextInt() is a method which takes integer input and stores in integer variable. The other data types Boolean, Float, Long and Double uses nextBoolean(), nextFloat(), nextLong() and nextDouble() to get input from user.
when expression is a conditional expression which returns a value corresponding to the satisfied expression. Kotlin, when expression is replacement of switch statement.
Comparison of switch and when
public static void main(){
int num = 1;
switch(num){
case 1:
System.out.println("Number One");
System.out.println("Multiple statements under one case");
break;
case 2:
System.out.println("Number Two");
break;
case 3:
case 4:
System.out.println("Number Three or Number 4");
break;
default:
System.out.println("None of the case satisfied.")
}
}
fun main(args : Array<String>){
val num : Int = 1
when(num) {
1 -> {
println("Number One")
println("Multiple statements under one case")
}
2 -> println("Number Two")
3,4 -> println("Number Three or Number 4")
else -> println("None of the case satisfied.")
}
}
in Kotlin:
- break statement not required
- instead of default we use else
- multiple cases can be separated by comma in kotlin
we can also use ranges in when
fun main(args : Array<String>){
val num : Int = 3
when(num) {
in 1..5 -> println("Input is provided in the range 1 to 5")
in 6..10 -> println("Input is provided in the range 6 to 10")
else -> println("none of the above")
}
}
A variable that doesn't refers to anything, refers to null. Null is a keyword to allow a variable refer to nothing or to check if it exists (comparison can be done with == or !=)
In Kotlin - A variable cannot actually be null unless it is declared to allow for null.
Null can be allowed by suffixing the type name with ?
Example:
fun nullSafety( val a : String, val b : String? ){ // here b is allowed for null
}
Let's see what calling to above function are allowed and what callings are not allowed
nullSafety("a", "b") // :white_check_mark: Allowed
nullSafety("a", null) // :white_check_mark: Allowed
nullSafety(null, "b") // :x: Not Allowed
nullSafety(null, null) // :x: Not Allowed
nullSafety(a, b) // :interrobang: is only allowed if the compiler can prove that a cannot possibly be null.
Inside of nullSafety, the compiler will not allow you to do anything with b that would result in an exception if b should happen to be null - so you can do a.length, but not b.length. However, once you're inside a conditional where you have checked that b is not null, you can do it:
fun nullSafety( val a : String, val b : String? ){
println(b.length) // :x: NOT ALLOWED
if (b != null) {
println(b.length) // :white_check_mark: ALLOWED AFTER CHECKING FOR NULL CONDITION
}
}
x?.y evaluates x, and if it is not null, then it evaluates x.y (without reevaluating x).
Basically safe call operator evaluates a chain by checking for null values.
x && x.y && x.y.z &&& x.y.z.p
:point_down: // this can be written by safe call operator as // :point_down:
x?.y?.z?.p
x ?: y evaluates x, which becomes the result of the expression unless it's null, in which case you'll get y instead (which ought to be of a non-nullable type). This is also known as the "Elvis operator". You can even use it to perform an early return in case of null:
val z = x ?: return y
This will assign x to z if x is non-null, but if it is null, the entire function that contains this expression will stop and return y (this works because return is also an expression, and if it is evaluated, it evaluates its argument and then makes the containing function return the result).
Sometimes we know that we have a value x that is not null, but compiler is not able to identify the logic, in that case we need to explicitly tell the compiler that it should not care about about the variable to have a null value.
!! operator is used for this purpose.
Example:
val x: String? = javaFunctionThatYouKnowReturnsNonNull()
val y: String = x!! // here the compiler will object as it is not sure if x is not null as x is declared to have null values.
!! will raise a NullPointerException if the value actually is null.
In Kotlin, functions are first-class values - they can be assigned to variables and passed around as parameters.
fun safeDivide(numerator: Int, denominator: Int) =
if (denominator == 0) 0.0 else numerator.toDouble() / denominator
we can assign a function to a variable by prefixing function's name with ::
// the type of the variable would normally be inferred in case of a function
val f: (Int, Int) -> Double = ::safeDivide
OR
val f = ::safeDivide
lambda expressions: unnamed function declarations with a very compact syntax, which evaluate to callable function objects.
- A lambda expression is enclosed in curly braces
- Begins by listing its parameter names and possibly their types (unless the types can be inferred from context):
- Parameters are not enclosed in paranthesis
- The last statement must be an expression, whose result will become the return value of the lambda (unless Unit is the return type of the variable/parameter that the lambda expression is assigned to, in which case the lambda has no return value).
val safeDivide = { numerator: Int, denominator: Int ->
if (denominator == 0) 0.0 else numerator.toDouble() / denominator
}
The type of safeDivide is (Int, Int) -> Double.
If the type is declared of the variable to which the lambda expression is attached then kotlin can infer the parameter types.
val drivingResult : (factor1 : String, factor2 : String ) -> Unit = { factor1 : String, factor2 : String ->
if( factor1 == "Good" && factor2 == "Good" ){
print("Passed")
}else if( (factor1 == "Good" && factor2 == "Bad") || (factor1 == "Bad" && factor2 == "Good") ){
print("Passed in half")
}else{
print("Failed")
}
}
// as we have declared the type of drivingResult we need not to mention the type of lambda function arguments
val drivingResult : (factor1 : String, factor2 : String ) -> Unit = { factor1, factor2 ->
if( factor1 == "Good" && factor2 == "Good" ){
print("Passed")
}else if( (factor1 == "Good" && factor2 == "Bad") || (factor1 == "Bad" && factor2 == "Good") ){
print("Passed in half")
}else{
print("Failed")
}
}
If we are passing the lambda function directly as parameter to a function then kotlin can infer the type of lambda arguments.
fun callAndPrint(function: (Int, Int) -> Double) {
println(function(2, 0))
}
callAndPrint({ numerator, denominator -> // here lambda function argument type is not required
if (denominator == 0) 0.0 else numerator.toDouble() / denominator
})
A parameterless lambda does not need the arrow
val hero: () -> Unit = { println("Lambda without parameter does not require -> ") }
OR
val hero = { println("Lambda without parameter does not require -> ") }
In Lambda with only one parameter, we can choose to omit the parameter and arrow (->)
// with arrow
val oneParameterLambda : (arg: Double) -> Double = { arg : Double ->
arg * arg
}
//without arrow and argument
val oneParameterLambda : (arg: Double) -> Double = { it*it }
// calling the function
oneParameterLambda(8.2);