Simple project showing some of the features Kotlin offers to Java developers. Where applicable, this will be accompanied by some Java code showing the difference in syntax and functionality between both languages.
This is intented to be a unidirectional comparison; I will focus on features in Kotlin and if possible map them to Java - not the other way around.
This is still very much a work-in-progress, but for the time being please have a look at the topics covered in this codebase. The list is below and should be fairly complete.
If you feel there are any mistakes, misconceptions, missing or unclear examples, feel free to let me know.
Kotlin has semicolon inference. It uses the pseudo-token SEMI (semicolon or newline) to separate declarations, statements and the like. Only in some cases, such as writing several statements on one line Kotlin requires an explicit semicolon.
fun doSomething() {
  val someString = "someString" // semicolon optional
if(true) { operation1(); operation() } // Semicolon required to separate statements
}
// Explicit, something can't be anything other than string, so type can be omitted
val something: String = "This is a string"
// Infers that something is of type String
val something = "This is a string"
Kotlin types are not nullable by default. When interoperating with Java, they can at times be null. To allow nulls in your code, append a '?' after the parameter type. Kotlin will force you to deal with possible null values.
fun operate(context: String?) { // context could be null }
When parameters are nullable, code must be written defensively to prevent nullpointers. The nullsafe operator will only access the function or property when the lefthand side of the statement is not null. Additionally ?: allows you to provide a default value in case any of the properties is null.
fun getContracteeName(contract: Contract?): String {
// ?. only executes if contract isn't null, ?: specifies a default in case of null
  return contract?.contractee?.name ?: "Unknown"
}
val person: Person() // <class>() implies constructor call
fun break() {
  throw IOException("")
}
if(instance is String) { // is is the equivalent for instanceof
  instance.toUpperCase() // inside the if-block instance is implicitly cast to String
}
var someProperty = "value"
someProperty = "newValue" // assign new value
val someProperty = "value"
someProperty = "newValue" // compiler error, someProperty is final
Kotlin advocates property access rather than accessor methods. If the need arises, it's possible to explicitly provide a getter for the property.
The implicit variable 'field' references the property.
var stringRep: String = "Default"
get() = field // return the actual value for the field
set(value) {
if(value.length < 10) // conditional set, if length < 10
field = value
}
If you need annotations on setters (for injection perhaps) there is a shorthand syntax.
var stringRep: String = "Default"
@Inject set // concise declaration for setter injection
Visibility can also be modified using similar syntax.
var stringRep: String = "Default"
private set // Setter has private access
Local properties are transient, unlike var/val they will not become properties in the class.
Primary constructors can't have a code block, so if any initializing logic is required, this can be encapsulated in the init block.
class Thing(nameParam: String) { // property is only in scope in constructor
val name = nameParam // nameParam can be used to initialize properties too
init {
println(nameParam) // nameParam is in scope in the init block as well
}
}
Constant declaration. This has a few limitations, as the value has to be:
- Top-level in a file - or object;
- Value has to be a string or a primitive;
- Can't declare custom getter for the property.
const val path = "/api/users" // Compile time constant
class UserResource {
@GetMapping(path) // Can be used as annotation values
fun findUsers(): List<User> { ... }
}
Top level (inside a package) declarations are marked public by default, so the keyword can be omitted in these cases.
public object PublicObject
public interface PublicInterface
public val someValue = "value"
public class PublicClass
private object PrivateObject
private interface PrivateInterface
private val privateValue = "value"
private class PrivateClass
Visible only within a module, which is basically defined as a set of classes compiled together in project module, maven module or similar.
internal val internalValue = ""
internal object InternalObject
internal class InternalClass
internal interface InternalInterface
Class members are public by default too, so here the keyword can also be omitted.
class PublicMembersClass {
public val publicValue ="PUBLIC"
public fun publicFunction() {}
public class PublicInnerClass
}
Private only to the enclosing class.
class PrivateMembersClass {
private val privateValue ="PRIVATE"
private fun privateFunction() {}
private class PrivateInnerClass
}
For classes that allow subclassing (i.e. are marked open) the protected modifier can be used to make the contents available inside any extending subclasses.
open class PrivateMembersClass {
protected val internalValue ="PROTECTED"
protected fun internalFunction() {}
protected class internalInnerClass
}
Members marked internal are only accessible in the same module.
class PrivateMembersClass {
internal val internalValue ="INTERNAL"
internal fun internalFunction() {}
internal class internalInnerClass
}
- Open
- Sealed
- object (singleton)
- abstract
- interface
- !!
- Elvis operator .?
- etc.
- examples
- if
- when
Indicates that a property will be initialized lazily and will not be null.
class MyComponent {
@Value("${config.property}")
lateinit var myVar: String
}
lateinit does not work with nullable, therefore the following is not allowed:
lateinit var myVar: String?
By lazy is a build in delegate that takes a lambda and returns an instance of Lazy.
Can be useful for expensive initialization where computation is required.
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
Invoking lazyValue will initialize only once.
fun main(args: Array<String>) {
println(lazyValue)
println(lazyValue)
}
Output is:
computed! Hello Hello
Lazy initialization comes with the cost of Synchronization. This behaviour can be changed when multi threading is not an issue.
Indicates a suspension point (this is currently an experimental feature from kotlinx).
To be used on a function that is used in the context of coroutines.
suspend fun someSlowFunction() { /* slow stuff happening */ }
- labels
- etc
TODO
class SomeType(val name: String)
class SomeType(val name: String)
val type = SomeType(name = "Name")
class SomeType(val name: String = "name")
class SomeType(val firstname: String, val lastname: String) {
private val fullname:String
init {
fullname = firstname + lastname
}
}
fun sum(left: Int, right: Int) { return left + right }
Usage: val total = sum(1, 3)
fun Int.add(add: Int) { return this + right }
Usage: val total = 1.add(3)
infix fun Int.and(add: Int) { return this + right }
Usage: val total = 1 and 3
fun modulo(input: Int): (Int) -> Boolean = {
it % input == 0
}
Usage: val values = (0..25).filter (modulo(5))
fun execute(function: () -> Unit) {
function()
}
Usage: execute { println("Lambda Syntax") }
fun function(){ println("Executed") }
Usage: execute(::function())
fun modulo(input: Int): (Int) -> Boolean = {
it % input == 0
}
val moduloReference = modulo(3)
Usage: val result = moduloReference.invoke(5)
data class Person(val name: String)
data class Person(
val name: String,
val lastname: String,
var email: String, // mutable
)
val person: Person("First", "Last", "[email protected]")
person.toString()
person.equals()
person.hashcode()
val person = Person("First", "Last", "[email protected]")
val copiedPerson = person.copy() // or:
val copiedPerson = person.copy(email = "[email protected]") // change email (or any given property)
class SomeType <out T>
class SomeType <in T>
class SomeType <T>
class SomeType <*>