From Java to Kotlin — Basic Syntax Reference

David Adeyinka
10 min readNov 24, 2020

--

Hello, my name is David, and this is the second installment in my small series on learning a new programming language based on a language you already know. Last week, I covered learning Python for those familiar with Java. Today, I’ll be helping people with a Java background who’ve decided to take on Kotlin.

Core Concepts

The core concepts I’ll be covering concerning the two languages, similar to my previous article, include:

  1. Major Differences
  2. Data Types
  3. Comments
  4. Variable Declaration and Initialization
  5. Type Conversion
  6. Operators and Operations
  7. Control Flow
  8. Functions
  9. Standard I/O
  10. Arrays

Cheat Sheet

I’ve created a single page cheat sheet to act as a quick reference for this whole article. You can find it here.

Let’s get started.

Major Differences

Before I start, please take note: Kotlin was designed to be compatible with Java, down to the bytecode level, and because of this, the two are very, very, similar. Kotlin, however, was designed to address the design flaws of Java and add more modern language features, so learning Kotlin will be less about what you can do just like you could do in Java but instead about what you can do better than you did in Java. With that in mind, let’s get to the differences:

  • Kotlin uses type inference. This means that you don’t have to declare the types of your variables, but you still can if you want, and you will sometimes need to.
  • Kotlin supports top-level functions and variables. Unlike Java, everything doesn’t have to be contained in a class.
  • The root class of the Kotlin hierarchy is the Any class, just like the Object class for Java. It also features an equals() , a toString() , and hashCode() method. It contains additional methods and properties and well, which are not relevant to this discussion.
  • One of the most notable features of Kotlin, when compared with Java, is null-safety: all variables must be declared nullable before they can be assigned the value null . Failure to do so will result in a compile-time exception instead of a runtime exception. More on this later.
  • static members aren’t present in Kotlin, rather, there are alternatives including top-level functions, companion objects and others.
  • Semicolons are optional.

More on the differences here.

Data Types

Java features primitive types like int , long , and double that are not classes, but have corresponding wrapper classes like Integer , Long , and Double respectively. Kotlin only has primitives that are classes, and they include Byte , Short , Int , Long , Float , Double , Char , and Boolean . They all correspond to their respective types in Java. The String class has the same name for both languages.

Comments

In addition to the types of comments Java supports, Kotlin also supports nested multiline comments:

/*New nested comment that won't be terminated by the old comment
/*Original comment for this code*/
fun display() {
println("Kotlin")
}
*/

Variable Declaration and Initialization

Java

import java.util.Scanner;class Program {
public static final int COUNT = 10; // 2
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String firstName; // 1, 3
String lastName = null; // 4
System.out.print("First Name: ");
firstName = scanner.nextLine();
System.out.print("Input Last Name(y/n)?: ");
final String lastNameInput = scanner.nextLine(); // 1
if(lastNameInput.equals("y")) {
System.out.print("Last Name: ");
lastName = scanner.nextLine();
}
final String username =
firstName + (lastName == null? "":lastName);
System.out.println("Username is: " + username);
scanner.close();
}
}

Kotlin

const val COUNT = 10                             // 2
class Program {
fun main() {
var firstName : String // 1, 3
var lastName : String? = null // 4
print("First Name: ")
firstName = readLine()
print("Input Last Name(y/n)?: ")
val lastNameInput = readLine()!! // 1, 3, 5
if(lastNameInput.equals("y")) {
print("Last Name: ")
lastName = readLine()
}
val username = firstName + (lastName?: "") // 6
println("Username is $username")
}
}
  1. Variables are declared with the Kotlin keyword var . Constants are declared with the keyword val , similar to the final keyword in Java.
  2. Constants whose value must be known at compile-time i.e. compile-time constants can have the const modifier added to their declaration, similar to static final constants in Java.
  3. Since Kotlin uses type inference, it’s optional to declare the type of a variable if it’s initialized at the time it is declared. Its type, must, however, be specified if the declaration and initialization are separate.
  4. A little talk on null-safety: objects that can be null must be declared nullable by giving them a nullable type. This is done by suffixing the type with a ? . In this example, the nullable string lastName must be given the type String? , or the code won’t compile.
  5. Objects with a nullable type can be converted to their non-nullable counterpart types in a few ways. One of those ways include suffixing the variable with a !! . This is done on the readLine() function which returns a to convert the String? type object it returns to a String . Note that if the nullable value was truly null when you used this operator, it will result in a KotlinNullPointerException , which basically defeats the purpose of null safety, so take care to use this operator only when you’re sure the variable will not be null .
  6. Another way to convert a nullable type object to a non-null type object is by giving the variable a default non-null value using the Elvis operator. It’s similar to the ternary operator in Java, but it’s only used for nullable types.
  7. The new keyword doesn’t exist in Kotlin. Objects are constructed without it.
//Java
Program p = new Program();
//Kotlin
val p = Program();

Type Conversion

  • The normal rules of implicit type conversion hold.
  • For explicit type conversion, the primitive number types include the following methods: toByte() , toShort() , toInt() , toLong() , toFloat() , toDouble() , toChar() .
  • Strings also have conversion functions of the form toX() where X is the type to be converted to and toXOrNull() which returns X as a null type when the conversion fails.
  • For objects, type casts are done using the as (“unsafe”) and as? (“safe” cast) operators. The difference between these two is that the safe cast operator will not throw an exception if the object being cast was truly null:
var y : String? = "Not Null"
var x : String = y as String
y = null
// var z : String? = y as String ERROR
var z : String? = y as? String // Safe
var a : String? = y as String? // Alternative way

Operators and Operations

  • The &&, || , and ! operators are the same in Java and Kotlin.
  • The bitwise operators have no special characters but all map to named functions. From Java to Kotlin: & maps to and , | maps to or , ^ maps to xor , >> maps to shr (shift right), << maps to shl (shift left), >>> maps to ushr , and ~ maps to inv . Examples:
5 and 6 // 4
4.inv() // -5
  • All arithmetic unary operators, binary operators, comparison operators, assignment(e.g i++ ) operators and equality operators are the same in both languages. They all have named function counterparts.
  • instanceof in Java corresponds to is in Kotlin.
  • In addition, Kotlin also has the range ( a..b , a and b inclusive) and membership ( in ) operators.
  • + is still used for String concatenation, but you can also use $ with curly braces for String templates:
var num = 3
var str = "This is number $num" // No braces required for a single variable
var strCurly = "This is num plus 3: ${num+3}" // Use curly braces when the expression is more complex

Control Flow

  • while and do-while loops are the same in both languages.
  • break and continue are the same in both languages.
  • switch-case is replaced by the more powerful when statement.
  • All forms of the if statement are treated as expressions that have values rather than normal statements which have no values:
val num : Int = if(x < y) {
3
} else {
6
}
  • if statements used this way must have an else block.
  • Note that this is the alternative for the Java ternary operator ( ?:). More than one statement can be in the if expression, but the last statement has to have the value being assigned.
val num : Int = if(x < y) {
println("Y is Greater")
3
} else if (x > y) {
println("X is Greater")
6
} else {
println("X and Y are equal")
9
}
  • In general in Kotlin, when a set of statements is used as an expression, the last line(s) must hold the value of the expression.
  • The when statement in Kotlin works like switch in Java, but with some more features:
  • when can be used to check object identity with the is operator:
val x = "String"
when(x) {
is Int -> {
println("x is numeric")
}
is String -> {
println("x is a String")
}
else -> {
println("x is an object")
}
}
  • when can also be used to test for membership of a sequence using the in or !in operator:
nums = arrayOf(1, 2, 3, 4, 5)
x = 6
when(x) {
in nums -> {
println("X is small")
}
in 11..15 -> {
println("X is large")
}
else -> {
println("X is moderate")
}
}
  • when statements can be used as expressions, but you have to include the else branch, just like the if statement.
  • for-each loops are the only kind of for loop in Kotlin. There’s no standard “counter” loop, but you can use ranges for counter behavior:
for (i in 0..10)          // 0 inclusive to 10 inclusive
...
for (i in 0 until 10) // 0 to 9 i.e. 0 inclusive to 10 exclusive
...
for (s : String in words)
...
for (i in nums.indices)

Functions

Java

class Program {
...
public static void main(String[] args) {...} // 1, 4
...
private static int sum(int a, int b) {...} // 1, 2, 3
...
public float average(int... nums) {...} // 5
...
private String name; // 7
public void setName(String name) {...} // 7
public String getName() {...} // 7
...
public void printWord() { printWord("apple") } // 4, 6
public void printWord(String word) {...} // 4, 6
}

Kotlin

fun main() {...}                                         // 1, 4
class Program {
...
companion object { // 1
fun sum(a : Int, b: Int) : Int {...} // 2, 3
}
...
fun average(vararg nums : Int) : Float {...} // 5
...
var name // 7
public get() {...} // 7
public set(value) {...} // 7
...
fun printWord(word : String = "apple") : Unit {...} // 4, 6
  1. As stated earlier, Kotlin supports top-level functions. main itself is a top-level function in Kotlin. This eliminates the need for the static modifier from Java, but a way to achieve this kind of behaviour is through companion object s. Every class can have at most one companion object .
  2. Functions are declared with the fun keyword followed by the function name and parameter list. The return type comes after the parameter list and is preceded by a colon.
  3. Function parameters are of the form <name> : <type> , similar to Kotlin variables.
  4. The return type void in Java is the same as Unit in Kotlin. It’s optional to specify this return type in Kotlin, however.
  5. Variable-length arguments are declared with the vararg keyword in Kotlin instead of the ... operator from Java.
  6. Kotlin supports default values for function parameters, therefore there’s no need for function overloading.
  7. Data members, or, fields of Kotlin classes are treated as properties. Properties discourage the use of “getter and setter” methods, and are instead declared public with a custom get and set function. The get and set functions are optional for each field, and must be declared immediately after their field if they are declared.
  8. You can also used named parameters in Kotlin to specify parameter values in any order:
val summation = Program.sum(b = 3, a = 2)

Standard I/O

Java

import java.util.Scanner;                    // 1
...
Scanner scanner = new Scanner(System.in); // 1
System.out.print("Same Line") // 2
System.out.println("New Line") // 2
String s = scanner.nextLine(); // 3

Kotlin

print("Same Line")    // 2
println("New Line") // 2
var s = readLine()!! // 3
  1. Java treats it’s most basic input and output like streams: System.out is a PrintStream and System.in is an InputStream . Kotlin’s basic input and output functions, however, don’t work with streams and are therefore less verbose.
  2. The print() and println() functions are the same in both languages. The main difference in syntax being the System.out object from Java preceding the method names.
  3. Kotlin’s readLine() function is the main way to collect input line by line. There are no functions to read for Int or Boolean objects like Java’s Scanner class has.

Arrays

Java

int[] nums = new int[32];                       // 1
for(int i = 0; i < 32; i++) {nums[i] = i * 2;} // 2
for(int i = 0; i < nums.length; i++) { // 4
System.out.println("I: " + nums[i]);
}
for(int i : nums) {System.out.println(i);}

Kotlin

var nums = Array<Int>(32) { i -> i * 2 }                     // 1, 3
for(i in nums.indices) { println("I: ${nums[i]}") } // 2, 5
for(i in 0 until nums.size) { println("I: ${nums.get(i)}") } // 4
  1. Kotlin’s arrays are also objects and are of type Array . This means you declare and initialize them like regular Kotlin objects.
  2. The standard [] index operator is overloaded with the methods get and set in Kotlin. You can use arrays either way.
  3. Kotlin Array s can take a lambda function in their constructor to initialize each member based on the Int parameter.
  4. You use size in Kotlin instead of length to find out the number of elements in an Array .
  5. You can iterate through the indices of an Array using the indices property to achieve the behavior of a counter loop.

Conclusion

Learning the similarities in syntax is only the start to learning a brand new language. I hope I’ve been able to help you get a firm grip on the basics as you move on to the advanced concepts in Kotlin, including coroutines, data classes, sealed classes, and much more. Thank you for your time.

Further Reading

--

--

No responses yet