Things to consider when calling Kotlin from Java.
import academy.learnprogramming.kotlincode.*;
import static academy.learnprogramming.kotlincode.CarKt.topLevel;
import static academy.learnprogramming.kotlincode.StaticCar.anotherTopLevel;
public class Main {
public static void main(String[] args) {
// So how do we call the top-level function from the Car.kt file?
topLevel(); // If we type the function name the IDE suggests an import (see above). Compiler will quietly generate static class for all top-level items and will give the class the same name as the Kotlin file name.
// So if we want to refer to a class we use the file name:
CarKt.topLevel();
//To demonstrate how we can deal with naming conflicts:
// anotherTopLevel(); // Without using JvmName to rename file IDE cannot locate this function
anotherTopLevel(); //Once JvmName has been implemented the IDE will suggest an import under the 'StaticCar' file name
CarKt.print("Print this Java string"); // We cannot invoke extension function like we would in Kotlin. Need to use standard call
Car car = new Car("blue", "BMW", 2011);
System.out.println(car.getModel()); // We call getters and setters from Java as standard as compiler has produced these for Kotlin class properties by default (val only generates getters). Wouldn't work if properties private
Car2 car2 = new Car2("red", "Ferrari", 2017, false);
car2.setColour("purple"); // If custom sets and gets are created Java will access those
// car2.setModel("A3"); // But not if setter is private
// car2.setYear(1999); // Doesn't exist as setters only generated for var not val
System.out.println(car2.getColour());
System.out.println(car2.isAutomatic()); // When dealing with Booleans generates getter name beginning with 'is'
// If we want Java to access the property directly without going through getter or setter, we can add an @JvmField annotation to the property:
int year = car2.year; // Can't use @JvmField with private properties or one that overrides another property or can be overridden, or one with const keyword
// How do we access functions within companion objects? Same for non-anonymous object instances (instances we create using the object keyword)?
// Must use @JvmStatic annotation. Results in two version being created. One in instance as normal, but also one as a Java static function.
Car2.Companion.carComp(); // Without @JvmStatic we're accessing the instance method generated by the compiler, not the static version. So we can't type Car2.carComp()
Car2.carComp2(); // With the @JvmStatic annotation. Means we're accessing the static version of the method.
// This could be necessary if carComp() was accessing static properties
// For named object instances i.e. Singletons:
SingletonObj.INSTANCE.doSomething(); // We can go through the INSTANCE object to access the functions within. Compiler creates an instance and assigns INSTANCE to it
SingletonObj2.doSomething2(); // If we annotate the function with @JvmStatic we can access it directly
// Fields, if they're not private:
System.out.println("is 4WD = " + Car2.Companion.is4WD()); // Without @JvmField
System.out.println("is pimped out = " + Car2.isPimpedOut); // With @JvmField
// If the field is a const you don't need to annotate it - automatically converted to static field:
System.out.println("a constant = " + Car2.constant);
// Also true for late initialised properties (whatever they are)
// Null safety
// Nothing stopping Java passing a null object to a nonNullable function parameter - Kotlin will generate an exception:
// car2.printMe(null);
// Kotlin doesn't require functions to declare what exceptions they can throw, whereas Java does. What happens when we call an exception-able Kotlin function from Java?
// CarKt.doIO(); // Throws IOException. Can we catch it?
// try {
// CarKt.doIO();
// } catch (IOException e) { // Error - 'IOException is never thrown' - Java can't see the potential exception
//
// }
try {
CarKt.doIO2();
} catch (IOException e) { // @Throws annotation solves the problem
System.out.println("IOException!");
}
// What happens when we've defined default parameters in a Kotlin function?
CarKt.defaultArgs("The number is: ", 40);
// CarKt.defaultArgs("The number is: "); // Error even though second argument should be optional
// When a Kotlin function with default values is called, only one version is generated for Java (requires all parameters). Annotate with @JvmOverloads:
CarKt.defaultArgs2("The number is: "); // All better
}
}
fun topLevel() = println("I'm in the car file")
fun main() {
"Print this".print() // Calling the extension function from within the Kotlin class, as standard
}
object SingletonObj {
fun doSomething() = println("I'm doing something in the Singleton object")
}
object SingletonObj2 {
@JvmStatic fun doSomething2() = println("I'm doing something in the second Singleton object")
}
fun doIO() {
throw IOException()
}
@Throws(IOException::class)
fun doIO2() {
throw IOException()
}
fun defaultArgs(str: String, num: Int = 25) {}
@JvmOverloads fun defaultArgs2(str: String, num: Int = 25) {}
class Car(val colour: String, var model: String, val year: Int) {
}
class Car2(colour: String, model: String, @JvmField val year: Int, val isAutomatic: Boolean) { // year property has been designated for direct access through Java
companion object {
const val constant = 25
val is4WD = false
@JvmField val isPimpedOut = true
fun carComp() = println("I'm in car's companion object")
@JvmStatic fun carComp2() = println("I'm also in car's companion object")
}
fun printMe(text: String) {
println("I don't expect a null value: $text")
}
var colour: String = colour
set(value) { // example custom setter which always produces same output
field = "always green"
}
var model: String = model
private set(value) {
field = "Focus"
}
}
fun String.print() { // Extension function
println(this)
}


