SlideShare a Scribd company logo
Kotlin Mysteries
found from years
working with
Kotlin
João Esperancinha 2024/04/24
https://jnation.pt/sessions/
Topics for today
Null safety
1. Working with the Spring Framework
2. Reflection to force nulls
Inline and cross-inline
1. The Java overview
Tail recursive => Tail Call Optimization
(TCO)
1. What is it
2. Why?
3. How it makes us work recursively and
not use mutable
Data classes
1. Why things work and why things don't
work
2. How to fix the ones that don't
3. How to work with use-site targets.
What does a `delegate` do? and other
use-site targets.
1. What is a delegate
2. How can a use-site target affect it
Kotlin & Java evolution
1. A main tale
About me
João Esperancinha
● Java
● Kotlin
● Groovy
● Scala
● Software Engineer 10+ years
● JESPROTECH owner for 2 years
Kong Champion/Java Professional/Spring Professional
What do I mean
with
Decompiling
Kotlin to ByteCode to Java
When we convert the
bytecode to Java, we get a
snapshot of how things would
work in the JVM. For a real
picture we would need to read
the bytecode directly, which
can be very difficult
Let’s DEMO this!
Null-safety
Kotlin promises a
guarantee of null-safety.
Although we can use
nullable members in our
classes, we really shouldn’t
whenever possible.
When is whenever
possible?
@Table(name = "CAR_PARTS")
@Entity
data class CarPart(
@Id
val id: Long,
val name: String,
val productionDate: Instant,
val expiryDate: Instant,
val barCode: Long,
val cost: BigDecimal
)
CREATE SEQUENCE car_parts_id_sequence
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
CREATE TABLE CAR_PARTS (
id BIGINT NOT NULL DEFAULT
nextval('car_parts_id_sequence'::regclass),
name VARCHAR(100),
production_date timestamp,
expiry_date timestamp,
bar_code BIGINT,
cost float
);
CRUD Entity Example
CRUD Entity Example
INSERT INTO CAR_PARTS
(name, production_date, expiry_date, bar_code, cost)
VALUES ('screw', current_date, current_date, 12345, 1.2);
INSERT INTO CAR_PARTS
(name, production_date, expiry_date, bar_code, cost)
VALUES (null, current_date, current_date, 12345, 1.2);
@Test
fun `should mysteriously get a list with a
car part with a name null`() {
carPartDao.findAll()
.filter { it.name == null }
.shouldHaveSize(1)
}
Is this possible?
Yes!
● Migrate data and database
model
● Use nullables
…
Reflection Example
val carPartDto = CarPartDto(
id = 123L,
name = "name",
productionDate = Instant.now(),
expiryDate = Instant.now(),
cost = BigDecimal.TEN,
barCode = 1234L
)
println(carPartDto)
val field: Field = CarPartDto::class.java
.getDeclaredField("name")
field.isAccessible = true
field.set(carPartDto, null)
println(carPartDto)
assert(carPartDto.name == null)
println(carPartDto.name == null)
data class CarPartDto(
val id: Long,
val name: String,
val productionDate:
Instant,
val expiryDate: Instant,
val barCode: Long,
val cost: BigDecimal
)
Is this possible?
Yes!
Inline and
crossinline.
Inline and crossinline can be used in
combination with each other. Inline
provides bytecode copies of the
code per each call point and they
can even help avoid type erasure.
Crossinline improves readability
and some safety, but nothing really
functional.
Why does this
matter?
Inlining code with inline
fun main() {
callEngineCrossInline {
println("Place key in ignition")
println("Turn key or press push button ignition")
println("Clutch to the floor")
println("Set the first gear")
}.run { println(this) }
}
inline fun callEngineCrossInline(startManually: () -> Unit) {
run loop@{
println("This is the start of the loop.")
introduction {
println("Get computer in the backseat")
return@introduction
}
println("This is the end of the loop.")
}
println("Engine started!")
}
fun introduction(intro: () -> Unit) {
println(LocalDateTime.now())
intro()
return
}
public final class IsolatedCarPartsExampleKt {
public static final void main() {
int $i$f$callEngineCrossInline = false;
int var1 = false;
String var2 = "This is the start of the loop.";
System.out.println(var2);
introduction((Function0)IsolatedCarPartsExampleKt$cal
lEngineCrossInline$1$1.INSTANCE);
var2 = "This is the end of the loop.";
System.out.println(var2);
String var4 = "Engine started!";
System.out.println(var4);
Unit var3 = Unit.INSTANCE;
int var5 = false;
System.out.println(var3);
}
public static final void introduction(@NotNull
Function0 intro) {
Intrinsics.checkNotNullParameter(intro,
"intro");
LocalDateTime var1 = LocalDateTime.now();
System.out.println(var1);
intro.invoke();
} public final void invoke() {
String var1 = "Get computer in the
backseat";
System.out.println(var1);
}
Decompiled
code
Crossinline as just a marker for inlined code
fun main() {
callEngineCrossInline {
println("Place key in ignition")
println("Turn key or press push button ignition")
println("Clutch to the floor")
println("Set the first gear")
}.run { println(this) }
}
inline fun callEngineCrossInline(crossinline startManually: ()
-> Unit) {
run loop@{
println("This is the start of the loop.")
introduction {
println("Get computer in the backseat")
startManually()
return@introduction
}
println("This is the end of the loop.")
}
println("Engine started!")
}
fun introduction(intro: () -> Unit) {
println(LocalDateTime.now())
intro()
return
}
public final class IsolatedCarPartsExampleKt {
public static final void main() {
int $i$f$callEngineCrossInline = false;
int var1 = false;
String var2 = "This is the start of the loop.";
System.out.println(var2);
introduction((Function0)(new
IsolatedCarPartsExampleKt$main$$inlined$callEngineCro
ssInline$1()));
var2 = "This is the end of the loop.";
System.out.println(var2);
String var4 = "Engine started!";
System.out.println(var4);
Unit var3 = Unit.INSTANCE;
int var5 = false;
System.out.println(var3);
}
public static final void introduction(@NotNull
Function0 intro) {
Intrinsics.checkNotNullParameter(intro,
"intro");
LocalDateTime var1 = LocalDateTime.now();
System.out.println(var1);
intro.invoke();
}
public final void invoke() {
String var1 = "Get computer in the
backseat";
System.out.println(var1);
int var2 = false;
String var3 = "Place key in ignition";
System.out.println(var3);
var3 = "Turn key or press push button
ignition";
System.out.println(var3);
var3 = "Clutch to the floor";
System.out.println(var3);
var3 = "Set the first gear";
System.out.println(var3);
}
Decompiled
code
Crossinline for safety
object SpecialShopNonLocalReturn {
inline fun goToStore(chooseItems: () -> Unit) {
println("Walks in")
chooseItems()
}
@JvmStatic
fun main(args: Array<String> = emptyArray()) {
goToStore {
println("Make purchase")
return@main
}
println("Never walks out")
}
}
object SpecialShopLocalReturn {
inline fun goToStore(crossinline block: () -> Unit) {
println("Walks in")
block()
}
@JvmStatic
fun main(args: Array<String> = emptyArray()) {
goToStore {
println("Make purchase")
return@goToStore
}
println("Walks out")
}
}
@JvmStatic
public static final void main(@NotNull String[] args) {
Intrinsics.checkNotNullParameter(args, "args");
SpecialShopNonLocalReturn this_$iv = INSTANCE;
int $i$f$goToStore = false;
String var3 = "Walks in";
System.out.println(var3);
int var4 = false;
String var5 = "Make purchase";
System.out.println(var5);
}
@JvmStatic
public static final void main(@NotNull String[] args) {
Intrinsics.checkNotNullParameter(args, "args");
SpecialShopLocalReturn this_$iv = INSTANCE;
int $i$f$goToStore = false;
String var3 = "Walks in";
System.out.println(var3);
int var4 = false;
String var5 = "Make purchase";
System.out.println(var5);
String var6 = "Walks out";
System.out.println(var6);
}
?
Decompiled
code
Tail Call
Optimization
Since the late 50’s TCO was
already a theory intentend to be
applied to Tail Recursivity. It
allows tail recursive functions to
be transformed into iterative
functions in the compiled code
for better performance.
What is the catch?
Tail Call Optimization
sealed interface Part {
val totalWeight: Double
}
sealed interface ComplexPart : Part{
val parts: List<Part>
}
data class CarPart(val name: String, val weight:
Double) : Part {
override val totalWeight: Double
get() = weight
}
data class ComplexCarPart(
val name: String,
val weight: Double,
override val parts: List<Part>
) :
ComplexPart {
override val totalWeight: Double
get() = weight
}
data class Car(
val name: String,
override val parts: List<Part>
) : ComplexPart {
override val totalWeight: Double
get() = parts.sumOf { it.totalWeight }
}
tailrec fun totalWeight(parts: List<Part>, acc: Double = 0.0):
Double {
if (parts.isEmpty()) {
return acc
}
val part = parts.first()
val remainingParts = parts.drop(1)
val currentWeight = acc + part.totalWeight
return when (part) {
is ComplexPart -> totalWeight(remainingParts +
part.parts, currentWeight)
else -> totalWeight(remainingParts, currentWeight)
}
}
Why use this?
All variables
are immutable
Tail recursive
Tail Call Optimization
tailrec fun totalWeight(parts: List<Part>, acc: Double = 0.0):
Double {
if (parts.isEmpty()) {
return acc
}
val part = parts.first()
val remainingParts = parts.drop(1)
val currentWeight = acc + part.totalWeight
return when (part) {
is ComplexPart -> totalWeight(remainingParts +
part.parts, currentWeight)
else -> totalWeight(remainingParts, currentWeight)
}
}
public static final double totalWeight(@NotNull List parts, double acc) {
while(true) {
Intrinsics.checkNotNullParameter(parts, "parts");
if (parts.isEmpty()) {
return acc;
}
Part part = (Part)CollectionsKt.first(parts);
List remainingParts = CollectionsKt.drop((Iterable)parts, 1);
double currentWeight = acc + part.getTotalWeight();
if (part instanceof ComplexPart) {
List var10000 = CollectionsKt.plus((Collection)remainingParts,
(Iterable)((ComplexPart)part).getParts());
acc = currentWeight;
parts = var10000;
} else {
acc = currentWeight;
parts = remainingParts;
}
}
}
Variables are
mutable and
algorithm is
iterative
Tail Call Optimization
Why use Tail Call Optimization(TCO) and why use
tailrec?
1. tailrec allow us to use tail recursive code and transform it in the
background to iterative code
2. tail recursive functions and enabling TCO on them prevents endless
recursive calls due to failed check of the terminating condition
3. By using tail recursive functions and applying tailrec we enable the
possible use of TCO and with it guarantee the use of a single stackframe
preventing StackOverflow exceptions
Data classes and
Frameworks
Kotlin provides use-site targets
that allow us to specify where
particular annotations have to
be applied. Sometimes we need
them and sometimes we don’t
Why?
Working with Data classes
@Table(name = "CAR_PARTS")
@Entity
data class CarPart(
@Id
val id: Long,
@Column
@field:NotNull
@field:Size(min=3, max=20)
val name: String,
val productionDate: Instant,
val expiryDate: Instant,
val barCode: Long,
@field:Min(value = 5)
val cost: BigDecimal
)
@Table(name = "CAR_PARTS")
@Entity
data class CarPart(
@Id
val id: Long,
@Column
@NotNull
@Size(min=3, max=20)
val name: String,
val productionDate: Instant,
val expiryDate: Instant,
val barCode: Long,
@Min(value = 5)
val cost: BigDecimal
)
Doesn’t work Works!
Why
use-site
targets?
Working with Data classes
https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets
If you don't specify a use-site target, the target is chosen
according to the @Target annotation of the annotation
being used. If there are multiple applicable targets, the first
applicable target from the following list is used:
● param
● property
● field
● https://github.com/Kotlin/KEEP/issues/402
Kotlin 2.2.0 as experimental
Working with Data classes
@Target({ElementType.METHOD,
ElementType.FIELD,
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.PARAMETER,
ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(List.class)
@Documented
@Constraint(
validatedBy = {}
)
public @interface Size {
PARAMETER
is
selected
Working with Data classes
public final class CarPart {
@Id
private final long id;
@Column
@NotNull
private final String name;
@NotNull
private final Instant productionDate;
@NotNull
private final Instant expiryDate;
private final long barCode;
@NotNull
private final BigDecimal cost;
public final long getId() {
return this.id;
}
@NotNull
public final String getName() {
return this.name;
}
@NotNull
public final Instant getProductionDate() {
return this.productionDate;
}
@NotNull
public final Instant getExpiryDate() {
return this.expiryDate;
}
public final long getBarCode() {
return this.barCode;
}
@NotNull
public final BigDecimal getCost() {
return this.cost;
}
public CarPart(long id, @jakarta.validation.constraints.NotNull @Size(min =
3,max = 20) @NotNull String name, @NotNull Instant productionDate, @NotNull
Instant expiryDate, long barCode, @Min(5L) @NotNull BigDecimal cost) {
Intrinsics.checkNotNullParameter(name, "name");
Intrinsics.checkNotNullParameter(productionDate, "productionDate");
Intrinsics.checkNotNullParameter(expiryDate, "expiryDate");
Intrinsics.checkNotNullParameter(cost, "cost");
Not where we want
them to be,
but where they are
expected
@Table(name = "CAR_PARTS")
@Entity
data class CarPart(
@Id
val id: Long,
@Column
@field:NotNull
@field:Size(min=3, max=20)
val name: String,
val productionDate: Instant,
val expiryDate: Instant,
val barCode: Long,
@field:Min(value = 5)
val cost: BigDecimal
)
Working with Data classes
public final class CarPart {
@Id
private final long id;
@Column
@NotNull
@Size(
min = 3,
max = 20
)
@org.jetbrains.annotations.NotNull
private final String name;
@org.jetbrains.annotations.NotNull
private final Instant productionDate;
@org.jetbrains.annotations.NotNull
private final Instant expiryDate;
private final long barCode;
@Min(5L)
@org.jetbrains.annotations.NotNull
private final BigDecimal cost;
Since @field forces
the target, these
annotations get
applied where they
should
Delegates and other
use-site targets
Delegation is a great part
of the Kotlin language
and it is quite different
than what we are used to
seeing in Java
But how can we use it?
Working with Delegates
interface Horn {
fun beep()
}
class CarHorn : Horn {
override fun beep() {
println("beep!")
}
}
class WagonHorn : Horn {
override fun beep() {
println("bwooooooo!")
}
}
annotation class DelegateToWagonHorn
annotation class DelegateToCarHorn
class HornPack {
@delegate:DelegateToWagonHorn
val wagonHorn: Horn by SoundDelegate(WagonHorn())
@delegate:DelegateToCarHorn
val carHorn: Horn by SoundDelegate(CarHorn())
}
class SoundDelegate(private val initialHorn: Horn) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): Horn {
return initialHorn
}
}
Where is this being applied to?
Horn or SoundDelegate?
Working with Delegates
public final class HornPack {
// $FF: synthetic field
static final KProperty[] $$delegatedProperties = new KProperty[]{Reflection.property1(new
PropertyReference1Impl(HornPack.class, "wagonHorn", "getWagonHorn()Lorg/jesperancinha/talks/carparts/Horn;",
0)), Reflection.property1(new PropertyReference1Impl(HornPack.class, "carHorn",
"getCarHorn()Lorg/jesperancinha/talks/carparts/Horn;", 0))};
@DelegateToWagonHorn
@NotNull
private final SoundDelegate wagonHorn$delegate = new SoundDelegate((Horn)(new WagonHorn()));
@DelegateToCarHorn
@NotNull
private final SoundDelegate carHorn$delegate = new SoundDelegate((Horn)(new CarHorn()));
@NotNull
public final Horn getWagonHorn() {
return this.wagonHorn$delegate.getValue(this, $$delegatedProperties[0]);
}
@NotNull
public final Horn getCarHorn() {
return this.carHorn$delegate.getValue(this, $$delegatedProperties[1]);
}
}
SoundDelegate
Not Horn!
Working with Delegates
class SanitizedName(var name: String?) {
operator fun getValue(thisRef: Any?,
property: KProperty<*>): String? = name
operator fun setValue(thisRef: Any?,
property: KProperty<*>, v: String?) {
name = v?.trim()
}
}
class PartNameDto {
@get:NotBlank
@get:Size(max = 12)
var name: String? by SanitizedName(null)
override fun toString(): String {
return name ?: "N/A"
}
}
class ImpossiblePartNameDto {
@delegate:NotBlank
@delegate:Size(max = 12)
var name: String? by SanitizedName(null)
override fun toString(): String {
return name ?: "N/A"
}
}
public final class PartNameDto {
static final KProperty[] $$delegatedProperties = new KProperty[]{Reflection.mutableProperty1(new
MutablePropertyReference1Impl(PartNameDto.class, "name", "getName()Ljava/lang/String;", 0))};
@Nullable
private final SanitizedName name$delegate = new SanitizedName((String)null);
@NotBlank
@Size(
max = 12
)
@Nullable
public final String getName() {
return this.name$delegate.getValue(this, $$delegatedProperties[0]);
}
…
public final class ImpossiblePartNameDto {
static final KProperty[] $$delegatedProperties = new KProperty[]{Reflection.mutableProperty1(new
MutablePropertyReference1Impl(ImpossiblePartNameDto.class, "name", "getName()Ljava/lang/String;",
0))};
@NotBlank
@Size(
max = 12
)
@Nullable
private final SanitizedName name$delegate = new SanitizedName((String)null);
@Nullable
public final String getName() {
return this.name$delegate.getValue(this, $$delegatedProperties[0]);
}
jakarta.validation.UnexpectedTypeException:
HV000030: No validator could be found for
constraint 'jakarta.validation.constraints.Size'
validating type 'SanitizedName'. Check
configuration for 'name$delegate'...
Working with Delegates
@Service
data class DelegationService(
val id: UUID = UUID.randomUUID()
) {
@delegate:LocalDateTimeValidatorConstraint
@get: Past
val currentDate: LocalDateTime by LocalDateTimeDelegate()
}
public class DelegationService {
// $FF: synthetic field
static final KProperty[] $$delegatedProperties = new KProperty[]{Reflection.property1(new
PropertyReference1Impl(DelegationService.class, "currentDate",
"getCurrentDate()Ljava/time/LocalDateTime;", 0))};
@LocalDateTimeValidatorConstraint
@NotNull
private final LocalDateTimeDelegate currentDate$delegate;
@NotNull
private final UUID id;
@Past
@NotNull
public LocalDateTime getCurrentDate() {
return this.currentDate$delegate.getValue(this, $$delegatedProperties[0]);
}
@Target(AnnotationTarget.FIELD, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@Constraint(validatedBy = [LocalDateTimeValidator::class])
@MustBeDocumented
annotation class LocalDateTimeValidatorConstraint(
val message: String = "Invalid value",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<*>> = []
)
class LocalDateTimeValidator : ConstraintValidator<LocalDateTimeValidatorConstraint, LocalDateTimeDelegate> {
override fun initialize(constraintAnnotation: LocalDateTimeValidatorConstraint) {
}
override fun isValid(value: LocalDateTimeDelegate, context: ConstraintValidatorContext): Boolean {
val country = Locale.getDefault().country.trim()
logger.info("Current country is {}", country)
return when (country) {
"", "NL", "US", "PT", "ES", "UK", "FR"-> true
else -> false
}
}
companion object {
val logger: Logger = getLogger<LocalDateTimeValidator>()
}
}
Unnamed classes Unnamed classes in
Kotlin were a focal point
in Java vs Kotlin
discussions
They have arrived in
Java
Main.java
void main() {
System.out.println("Hello, world!");
}
Unnamed Classes
Main.kt
fun main(args: Array<String>) {
runApplication<CarPartsDataStructureApplication>(*args)
}
JEP 445: Unnamed
Classes and Instance
Main Methods
Friendly competition
drives innovation, but
also a lot of synergy
What’s next?
● Better understanding of the Kotlin Language.
● Better understanding of the Spring Framework, Quarkus, Ktor and where, when
and how to use them.
● Search the Kotlin documentation before searching for the seemingly
unexplainable.
● Nothing is perfect and Kotlin also falls into that category and recognizing that,
allow us to be better.
● Check if Kotlin or Java are the right languages for you and your team
Questions and answers
Q: Is the use of tailrec only intended to make sure we use readable code as for
example with recursive functions and then use it iteratively when compiled to the
bytecode?
A: A quick answer is yes. The nuanced answer is that, while tailrec enables the
compiler to apply the optimization only if the function is tail recursive, it is
only with the application of TCO, that we can make sure to avoid StackOverflow
exceptions, because with TCO we use only one StackFrame. It is TCO that does this
and not exactly the application of tailrec.
Questions?
I am an inquisitive cat
Resources
● Null Safety: https://kotlinlang.org/docs/null-safety.html
● Inline: https://kotlinlang.org/docs/inline-functions.html
● Tail Call Optimization: https://kotlinlang.org/docs/functions.html#tail-recursive-functions
● Annotation use-site targets:
https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets
● Spring Validation via AOP :
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-validation.html
● https://github.com/Kotlin/KEEP/issues/402
● https://openjdk.org/jeps/445
Source code and slides
● https://github.com/jesperancinha/kotlin-mysteries
About me
● Homepage - https://joaofilipesabinoesperancinha.nl
● LinkedIn - https://www.linkedin.com/in/joaoesperancinha/
● YouTube - JESPROTECH - https://www.youtube.com/@jesprotech
● Bluesky - https://bsky.app/profile/jesperancinha.bsky.social
● Mastodon - https://masto.ai/@jesperancinha
● GitHub - https://github.com/jesperancinha
● Hackernoon - https://hackernoon.com/u/jesperancinha
● DevTO - https://dev.to/jofisaes
● Medium - https://medium.com/@jofisaes
Thank you!

More Related Content

PPTX
Decoding Kotlin - Your guide to solving the mysterious in Kotlin - JNation2025
PPTX
Decoding Kotlin - Your guide to solving the mysterious in Kotlin.pptx
PPTX
KotlinForJavaDevelopers-UJUG.pptx
PDF
Kotlin boost yourproductivity
PPTX
Performance #5 cpu and battery
PDF
Kotlin: forse è la volta buona (Trento)
PDF
Exploring Koltin on Android
PDF
Kotlin: maybe it's the right time
Decoding Kotlin - Your guide to solving the mysterious in Kotlin - JNation2025
Decoding Kotlin - Your guide to solving the mysterious in Kotlin.pptx
KotlinForJavaDevelopers-UJUG.pptx
Kotlin boost yourproductivity
Performance #5 cpu and battery
Kotlin: forse è la volta buona (Trento)
Exploring Koltin on Android
Kotlin: maybe it's the right time

Similar to Decoding Kotlin - Your Guide to Solving the Mysterious in Kotlin - Devoxx PL 2025 (20)

PDF
Kotlin, smarter development for the jvm
PDF
Kotlin for Android Developers - 3
PDF
Kotlin for android developers whats new
PDF
2022 May - Shoulders of Giants - Amsterdam - Kotlin Dev Day.pdf
PPTX
Scala training workshop 02
PPTX
Kotlin coroutines and spring framework
PDF
Kotlin Backend Development 6 Yrs Recap. The Good, the Bad and the Ugly
PDF
Develop your next app with kotlin @ AndroidMakersFr 2017
PPTX
Kotlin decompiled
PDF
Having Fun with Kotlin Android - DILo Surabaya
PDF
Kotlin: A pragmatic language by JetBrains
PPTX
Introduction to Kotlin Language and its application to Android platform
PDF
Kotlin Advanced - Apalon Kotlin Sprint Part 3
PDF
Kotlin intro
PDF
Боремся с NPE вместе с Kotlin, Павел Шацких СберТех
PDF
Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...
PPTX
Real world DSL - making technical and business people speaking the same language
PPTX
Kotlin Language Features - A Java comparison
PPTX
2019-01-29 - Demystifying Kotlin Coroutines
PDF
Let's fly to the Kotlin Island. Just an introduction to Kotlin
Kotlin, smarter development for the jvm
Kotlin for Android Developers - 3
Kotlin for android developers whats new
2022 May - Shoulders of Giants - Amsterdam - Kotlin Dev Day.pdf
Scala training workshop 02
Kotlin coroutines and spring framework
Kotlin Backend Development 6 Yrs Recap. The Good, the Bad and the Ugly
Develop your next app with kotlin @ AndroidMakersFr 2017
Kotlin decompiled
Having Fun with Kotlin Android - DILo Surabaya
Kotlin: A pragmatic language by JetBrains
Introduction to Kotlin Language and its application to Android platform
Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin intro
Боремся с NPE вместе с Kotlin, Павел Шацких СберТех
Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...
Real world DSL - making technical and business people speaking the same language
Kotlin Language Features - A Java comparison
2019-01-29 - Demystifying Kotlin Coroutines
Let's fly to the Kotlin Island. Just an introduction to Kotlin
Ad

More from João Esperancinha (15)

PPTX
Could Virtual Threads cast away the usage of Kotlin Coroutines - DevoxxUK2025
PPTX
Boosting performance and functional style with Project Arrow from a practical...
PPTX
Start from the Package in Spring CDS - Basic course
PPTX
Apollo 4 Kotlin made me Graphql and I learned how to use it
PPTX
Monads are no Nomads - Unlocking the basics
PPTX
C.R.a.C in Spring - I froze my server! (15 minute session for NLJUG speaker a...
PPTX
Could Virtual Threads cast away the usage of Kotlin Coroutines
PPTX
Managing gRPC Services using Kong KONNECT and the KONG API Gateway
PPTX
Kuma Meshes Part I - The basics - A tutorial
PPTX
Fields in Java and Kotlin and what to expect.pptx
PPTX
Demystifying Co, Contra, In Kotlin modifier keywords.pptx
PPTX
Unlocking the Power of Kotlin Channels.pptx
PPTX
Exploring Tailrec Through Time Until Kotlin.pptx
PPTX
Reactive programming with Spring Webflux.pptx
PPTX
KONNECT Kong-Presentation How to protect web applications
Could Virtual Threads cast away the usage of Kotlin Coroutines - DevoxxUK2025
Boosting performance and functional style with Project Arrow from a practical...
Start from the Package in Spring CDS - Basic course
Apollo 4 Kotlin made me Graphql and I learned how to use it
Monads are no Nomads - Unlocking the basics
C.R.a.C in Spring - I froze my server! (15 minute session for NLJUG speaker a...
Could Virtual Threads cast away the usage of Kotlin Coroutines
Managing gRPC Services using Kong KONNECT and the KONG API Gateway
Kuma Meshes Part I - The basics - A tutorial
Fields in Java and Kotlin and what to expect.pptx
Demystifying Co, Contra, In Kotlin modifier keywords.pptx
Unlocking the Power of Kotlin Channels.pptx
Exploring Tailrec Through Time Until Kotlin.pptx
Reactive programming with Spring Webflux.pptx
KONNECT Kong-Presentation How to protect web applications
Ad

Recently uploaded (20)

PDF
Mohammad Mahdi Farshadian CV - Prospective PhD Student 2026
PDF
BMEC211 - INTRODUCTION TO MECHATRONICS-1.pdf
PPT
CRASH COURSE IN ALTERNATIVE PLUMBING CLASS
PPT
Project quality management in manufacturing
PDF
TFEC-4-2020-Design-Guide-for-Timber-Roof-Trusses.pdf
PPTX
Internet of Things (IOT) - A guide to understanding
PPTX
web development for engineering and engineering
PPTX
bas. eng. economics group 4 presentation 1.pptx
PDF
PPT on Performance Review to get promotions
PDF
R24 SURVEYING LAB MANUAL for civil enggi
PDF
composite construction of structures.pdf
PDF
Well-logging-methods_new................
PDF
Embodied AI: Ushering in the Next Era of Intelligent Systems
DOCX
573137875-Attendance-Management-System-original
PPTX
FINAL REVIEW FOR COPD DIANOSIS FOR PULMONARY DISEASE.pptx
PPTX
CH1 Production IntroductoryConcepts.pptx
PPTX
Infosys Presentation by1.Riyan Bagwan 2.Samadhan Naiknavare 3.Gaurav Shinde 4...
PPTX
KTU 2019 -S7-MCN 401 MODULE 2-VINAY.pptx
PPTX
IOT PPTs Week 10 Lecture Material.pptx of NPTEL Smart Cities contd
PPTX
Construction Project Organization Group 2.pptx
Mohammad Mahdi Farshadian CV - Prospective PhD Student 2026
BMEC211 - INTRODUCTION TO MECHATRONICS-1.pdf
CRASH COURSE IN ALTERNATIVE PLUMBING CLASS
Project quality management in manufacturing
TFEC-4-2020-Design-Guide-for-Timber-Roof-Trusses.pdf
Internet of Things (IOT) - A guide to understanding
web development for engineering and engineering
bas. eng. economics group 4 presentation 1.pptx
PPT on Performance Review to get promotions
R24 SURVEYING LAB MANUAL for civil enggi
composite construction of structures.pdf
Well-logging-methods_new................
Embodied AI: Ushering in the Next Era of Intelligent Systems
573137875-Attendance-Management-System-original
FINAL REVIEW FOR COPD DIANOSIS FOR PULMONARY DISEASE.pptx
CH1 Production IntroductoryConcepts.pptx
Infosys Presentation by1.Riyan Bagwan 2.Samadhan Naiknavare 3.Gaurav Shinde 4...
KTU 2019 -S7-MCN 401 MODULE 2-VINAY.pptx
IOT PPTs Week 10 Lecture Material.pptx of NPTEL Smart Cities contd
Construction Project Organization Group 2.pptx

Decoding Kotlin - Your Guide to Solving the Mysterious in Kotlin - Devoxx PL 2025

  • 1. Kotlin Mysteries found from years working with Kotlin João Esperancinha 2024/04/24 https://jnation.pt/sessions/
  • 2. Topics for today Null safety 1. Working with the Spring Framework 2. Reflection to force nulls Inline and cross-inline 1. The Java overview Tail recursive => Tail Call Optimization (TCO) 1. What is it 2. Why? 3. How it makes us work recursively and not use mutable Data classes 1. Why things work and why things don't work 2. How to fix the ones that don't 3. How to work with use-site targets. What does a `delegate` do? and other use-site targets. 1. What is a delegate 2. How can a use-site target affect it Kotlin & Java evolution 1. A main tale
  • 3. About me João Esperancinha ● Java ● Kotlin ● Groovy ● Scala ● Software Engineer 10+ years ● JESPROTECH owner for 2 years Kong Champion/Java Professional/Spring Professional
  • 4. What do I mean with Decompiling Kotlin to ByteCode to Java When we convert the bytecode to Java, we get a snapshot of how things would work in the JVM. For a real picture we would need to read the bytecode directly, which can be very difficult Let’s DEMO this!
  • 5. Null-safety Kotlin promises a guarantee of null-safety. Although we can use nullable members in our classes, we really shouldn’t whenever possible. When is whenever possible?
  • 6. @Table(name = "CAR_PARTS") @Entity data class CarPart( @Id val id: Long, val name: String, val productionDate: Instant, val expiryDate: Instant, val barCode: Long, val cost: BigDecimal ) CREATE SEQUENCE car_parts_id_sequence START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; CREATE TABLE CAR_PARTS ( id BIGINT NOT NULL DEFAULT nextval('car_parts_id_sequence'::regclass), name VARCHAR(100), production_date timestamp, expiry_date timestamp, bar_code BIGINT, cost float ); CRUD Entity Example
  • 7. CRUD Entity Example INSERT INTO CAR_PARTS (name, production_date, expiry_date, bar_code, cost) VALUES ('screw', current_date, current_date, 12345, 1.2); INSERT INTO CAR_PARTS (name, production_date, expiry_date, bar_code, cost) VALUES (null, current_date, current_date, 12345, 1.2); @Test fun `should mysteriously get a list with a car part with a name null`() { carPartDao.findAll() .filter { it.name == null } .shouldHaveSize(1) } Is this possible? Yes! ● Migrate data and database model ● Use nullables …
  • 8. Reflection Example val carPartDto = CarPartDto( id = 123L, name = "name", productionDate = Instant.now(), expiryDate = Instant.now(), cost = BigDecimal.TEN, barCode = 1234L ) println(carPartDto) val field: Field = CarPartDto::class.java .getDeclaredField("name") field.isAccessible = true field.set(carPartDto, null) println(carPartDto) assert(carPartDto.name == null) println(carPartDto.name == null) data class CarPartDto( val id: Long, val name: String, val productionDate: Instant, val expiryDate: Instant, val barCode: Long, val cost: BigDecimal ) Is this possible? Yes!
  • 9. Inline and crossinline. Inline and crossinline can be used in combination with each other. Inline provides bytecode copies of the code per each call point and they can even help avoid type erasure. Crossinline improves readability and some safety, but nothing really functional. Why does this matter?
  • 10. Inlining code with inline fun main() { callEngineCrossInline { println("Place key in ignition") println("Turn key or press push button ignition") println("Clutch to the floor") println("Set the first gear") }.run { println(this) } } inline fun callEngineCrossInline(startManually: () -> Unit) { run loop@{ println("This is the start of the loop.") introduction { println("Get computer in the backseat") return@introduction } println("This is the end of the loop.") } println("Engine started!") } fun introduction(intro: () -> Unit) { println(LocalDateTime.now()) intro() return } public final class IsolatedCarPartsExampleKt { public static final void main() { int $i$f$callEngineCrossInline = false; int var1 = false; String var2 = "This is the start of the loop."; System.out.println(var2); introduction((Function0)IsolatedCarPartsExampleKt$cal lEngineCrossInline$1$1.INSTANCE); var2 = "This is the end of the loop."; System.out.println(var2); String var4 = "Engine started!"; System.out.println(var4); Unit var3 = Unit.INSTANCE; int var5 = false; System.out.println(var3); } public static final void introduction(@NotNull Function0 intro) { Intrinsics.checkNotNullParameter(intro, "intro"); LocalDateTime var1 = LocalDateTime.now(); System.out.println(var1); intro.invoke(); } public final void invoke() { String var1 = "Get computer in the backseat"; System.out.println(var1); } Decompiled code
  • 11. Crossinline as just a marker for inlined code fun main() { callEngineCrossInline { println("Place key in ignition") println("Turn key or press push button ignition") println("Clutch to the floor") println("Set the first gear") }.run { println(this) } } inline fun callEngineCrossInline(crossinline startManually: () -> Unit) { run loop@{ println("This is the start of the loop.") introduction { println("Get computer in the backseat") startManually() return@introduction } println("This is the end of the loop.") } println("Engine started!") } fun introduction(intro: () -> Unit) { println(LocalDateTime.now()) intro() return } public final class IsolatedCarPartsExampleKt { public static final void main() { int $i$f$callEngineCrossInline = false; int var1 = false; String var2 = "This is the start of the loop."; System.out.println(var2); introduction((Function0)(new IsolatedCarPartsExampleKt$main$$inlined$callEngineCro ssInline$1())); var2 = "This is the end of the loop."; System.out.println(var2); String var4 = "Engine started!"; System.out.println(var4); Unit var3 = Unit.INSTANCE; int var5 = false; System.out.println(var3); } public static final void introduction(@NotNull Function0 intro) { Intrinsics.checkNotNullParameter(intro, "intro"); LocalDateTime var1 = LocalDateTime.now(); System.out.println(var1); intro.invoke(); } public final void invoke() { String var1 = "Get computer in the backseat"; System.out.println(var1); int var2 = false; String var3 = "Place key in ignition"; System.out.println(var3); var3 = "Turn key or press push button ignition"; System.out.println(var3); var3 = "Clutch to the floor"; System.out.println(var3); var3 = "Set the first gear"; System.out.println(var3); } Decompiled code
  • 12. Crossinline for safety object SpecialShopNonLocalReturn { inline fun goToStore(chooseItems: () -> Unit) { println("Walks in") chooseItems() } @JvmStatic fun main(args: Array<String> = emptyArray()) { goToStore { println("Make purchase") return@main } println("Never walks out") } } object SpecialShopLocalReturn { inline fun goToStore(crossinline block: () -> Unit) { println("Walks in") block() } @JvmStatic fun main(args: Array<String> = emptyArray()) { goToStore { println("Make purchase") return@goToStore } println("Walks out") } } @JvmStatic public static final void main(@NotNull String[] args) { Intrinsics.checkNotNullParameter(args, "args"); SpecialShopNonLocalReturn this_$iv = INSTANCE; int $i$f$goToStore = false; String var3 = "Walks in"; System.out.println(var3); int var4 = false; String var5 = "Make purchase"; System.out.println(var5); } @JvmStatic public static final void main(@NotNull String[] args) { Intrinsics.checkNotNullParameter(args, "args"); SpecialShopLocalReturn this_$iv = INSTANCE; int $i$f$goToStore = false; String var3 = "Walks in"; System.out.println(var3); int var4 = false; String var5 = "Make purchase"; System.out.println(var5); String var6 = "Walks out"; System.out.println(var6); } ? Decompiled code
  • 13. Tail Call Optimization Since the late 50’s TCO was already a theory intentend to be applied to Tail Recursivity. It allows tail recursive functions to be transformed into iterative functions in the compiled code for better performance. What is the catch?
  • 14. Tail Call Optimization sealed interface Part { val totalWeight: Double } sealed interface ComplexPart : Part{ val parts: List<Part> } data class CarPart(val name: String, val weight: Double) : Part { override val totalWeight: Double get() = weight } data class ComplexCarPart( val name: String, val weight: Double, override val parts: List<Part> ) : ComplexPart { override val totalWeight: Double get() = weight } data class Car( val name: String, override val parts: List<Part> ) : ComplexPart { override val totalWeight: Double get() = parts.sumOf { it.totalWeight } } tailrec fun totalWeight(parts: List<Part>, acc: Double = 0.0): Double { if (parts.isEmpty()) { return acc } val part = parts.first() val remainingParts = parts.drop(1) val currentWeight = acc + part.totalWeight return when (part) { is ComplexPart -> totalWeight(remainingParts + part.parts, currentWeight) else -> totalWeight(remainingParts, currentWeight) } } Why use this? All variables are immutable Tail recursive
  • 15. Tail Call Optimization tailrec fun totalWeight(parts: List<Part>, acc: Double = 0.0): Double { if (parts.isEmpty()) { return acc } val part = parts.first() val remainingParts = parts.drop(1) val currentWeight = acc + part.totalWeight return when (part) { is ComplexPart -> totalWeight(remainingParts + part.parts, currentWeight) else -> totalWeight(remainingParts, currentWeight) } } public static final double totalWeight(@NotNull List parts, double acc) { while(true) { Intrinsics.checkNotNullParameter(parts, "parts"); if (parts.isEmpty()) { return acc; } Part part = (Part)CollectionsKt.first(parts); List remainingParts = CollectionsKt.drop((Iterable)parts, 1); double currentWeight = acc + part.getTotalWeight(); if (part instanceof ComplexPart) { List var10000 = CollectionsKt.plus((Collection)remainingParts, (Iterable)((ComplexPart)part).getParts()); acc = currentWeight; parts = var10000; } else { acc = currentWeight; parts = remainingParts; } } } Variables are mutable and algorithm is iterative
  • 16. Tail Call Optimization Why use Tail Call Optimization(TCO) and why use tailrec? 1. tailrec allow us to use tail recursive code and transform it in the background to iterative code 2. tail recursive functions and enabling TCO on them prevents endless recursive calls due to failed check of the terminating condition 3. By using tail recursive functions and applying tailrec we enable the possible use of TCO and with it guarantee the use of a single stackframe preventing StackOverflow exceptions
  • 17. Data classes and Frameworks Kotlin provides use-site targets that allow us to specify where particular annotations have to be applied. Sometimes we need them and sometimes we don’t Why?
  • 18. Working with Data classes @Table(name = "CAR_PARTS") @Entity data class CarPart( @Id val id: Long, @Column @field:NotNull @field:Size(min=3, max=20) val name: String, val productionDate: Instant, val expiryDate: Instant, val barCode: Long, @field:Min(value = 5) val cost: BigDecimal ) @Table(name = "CAR_PARTS") @Entity data class CarPart( @Id val id: Long, @Column @NotNull @Size(min=3, max=20) val name: String, val productionDate: Instant, val expiryDate: Instant, val barCode: Long, @Min(value = 5) val cost: BigDecimal ) Doesn’t work Works! Why use-site targets?
  • 19. Working with Data classes https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets If you don't specify a use-site target, the target is chosen according to the @Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following list is used: ● param ● property ● field ● https://github.com/Kotlin/KEEP/issues/402 Kotlin 2.2.0 as experimental
  • 20. Working with Data classes @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(List.class) @Documented @Constraint( validatedBy = {} ) public @interface Size { PARAMETER is selected
  • 21. Working with Data classes public final class CarPart { @Id private final long id; @Column @NotNull private final String name; @NotNull private final Instant productionDate; @NotNull private final Instant expiryDate; private final long barCode; @NotNull private final BigDecimal cost; public final long getId() { return this.id; } @NotNull public final String getName() { return this.name; } @NotNull public final Instant getProductionDate() { return this.productionDate; } @NotNull public final Instant getExpiryDate() { return this.expiryDate; } public final long getBarCode() { return this.barCode; } @NotNull public final BigDecimal getCost() { return this.cost; } public CarPart(long id, @jakarta.validation.constraints.NotNull @Size(min = 3,max = 20) @NotNull String name, @NotNull Instant productionDate, @NotNull Instant expiryDate, long barCode, @Min(5L) @NotNull BigDecimal cost) { Intrinsics.checkNotNullParameter(name, "name"); Intrinsics.checkNotNullParameter(productionDate, "productionDate"); Intrinsics.checkNotNullParameter(expiryDate, "expiryDate"); Intrinsics.checkNotNullParameter(cost, "cost"); Not where we want them to be, but where they are expected
  • 22. @Table(name = "CAR_PARTS") @Entity data class CarPart( @Id val id: Long, @Column @field:NotNull @field:Size(min=3, max=20) val name: String, val productionDate: Instant, val expiryDate: Instant, val barCode: Long, @field:Min(value = 5) val cost: BigDecimal ) Working with Data classes public final class CarPart { @Id private final long id; @Column @NotNull @Size( min = 3, max = 20 ) @org.jetbrains.annotations.NotNull private final String name; @org.jetbrains.annotations.NotNull private final Instant productionDate; @org.jetbrains.annotations.NotNull private final Instant expiryDate; private final long barCode; @Min(5L) @org.jetbrains.annotations.NotNull private final BigDecimal cost; Since @field forces the target, these annotations get applied where they should
  • 23. Delegates and other use-site targets Delegation is a great part of the Kotlin language and it is quite different than what we are used to seeing in Java But how can we use it?
  • 24. Working with Delegates interface Horn { fun beep() } class CarHorn : Horn { override fun beep() { println("beep!") } } class WagonHorn : Horn { override fun beep() { println("bwooooooo!") } } annotation class DelegateToWagonHorn annotation class DelegateToCarHorn class HornPack { @delegate:DelegateToWagonHorn val wagonHorn: Horn by SoundDelegate(WagonHorn()) @delegate:DelegateToCarHorn val carHorn: Horn by SoundDelegate(CarHorn()) } class SoundDelegate(private val initialHorn: Horn) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Horn { return initialHorn } } Where is this being applied to? Horn or SoundDelegate?
  • 25. Working with Delegates public final class HornPack { // $FF: synthetic field static final KProperty[] $$delegatedProperties = new KProperty[]{Reflection.property1(new PropertyReference1Impl(HornPack.class, "wagonHorn", "getWagonHorn()Lorg/jesperancinha/talks/carparts/Horn;", 0)), Reflection.property1(new PropertyReference1Impl(HornPack.class, "carHorn", "getCarHorn()Lorg/jesperancinha/talks/carparts/Horn;", 0))}; @DelegateToWagonHorn @NotNull private final SoundDelegate wagonHorn$delegate = new SoundDelegate((Horn)(new WagonHorn())); @DelegateToCarHorn @NotNull private final SoundDelegate carHorn$delegate = new SoundDelegate((Horn)(new CarHorn())); @NotNull public final Horn getWagonHorn() { return this.wagonHorn$delegate.getValue(this, $$delegatedProperties[0]); } @NotNull public final Horn getCarHorn() { return this.carHorn$delegate.getValue(this, $$delegatedProperties[1]); } } SoundDelegate Not Horn!
  • 26. Working with Delegates class SanitizedName(var name: String?) { operator fun getValue(thisRef: Any?, property: KProperty<*>): String? = name operator fun setValue(thisRef: Any?, property: KProperty<*>, v: String?) { name = v?.trim() } } class PartNameDto { @get:NotBlank @get:Size(max = 12) var name: String? by SanitizedName(null) override fun toString(): String { return name ?: "N/A" } } class ImpossiblePartNameDto { @delegate:NotBlank @delegate:Size(max = 12) var name: String? by SanitizedName(null) override fun toString(): String { return name ?: "N/A" } } public final class PartNameDto { static final KProperty[] $$delegatedProperties = new KProperty[]{Reflection.mutableProperty1(new MutablePropertyReference1Impl(PartNameDto.class, "name", "getName()Ljava/lang/String;", 0))}; @Nullable private final SanitizedName name$delegate = new SanitizedName((String)null); @NotBlank @Size( max = 12 ) @Nullable public final String getName() { return this.name$delegate.getValue(this, $$delegatedProperties[0]); } … public final class ImpossiblePartNameDto { static final KProperty[] $$delegatedProperties = new KProperty[]{Reflection.mutableProperty1(new MutablePropertyReference1Impl(ImpossiblePartNameDto.class, "name", "getName()Ljava/lang/String;", 0))}; @NotBlank @Size( max = 12 ) @Nullable private final SanitizedName name$delegate = new SanitizedName((String)null); @Nullable public final String getName() { return this.name$delegate.getValue(this, $$delegatedProperties[0]); } jakarta.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'jakarta.validation.constraints.Size' validating type 'SanitizedName'. Check configuration for 'name$delegate'...
  • 27. Working with Delegates @Service data class DelegationService( val id: UUID = UUID.randomUUID() ) { @delegate:LocalDateTimeValidatorConstraint @get: Past val currentDate: LocalDateTime by LocalDateTimeDelegate() } public class DelegationService { // $FF: synthetic field static final KProperty[] $$delegatedProperties = new KProperty[]{Reflection.property1(new PropertyReference1Impl(DelegationService.class, "currentDate", "getCurrentDate()Ljava/time/LocalDateTime;", 0))}; @LocalDateTimeValidatorConstraint @NotNull private final LocalDateTimeDelegate currentDate$delegate; @NotNull private final UUID id; @Past @NotNull public LocalDateTime getCurrentDate() { return this.currentDate$delegate.getValue(this, $$delegatedProperties[0]); } @Target(AnnotationTarget.FIELD, AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) @Constraint(validatedBy = [LocalDateTimeValidator::class]) @MustBeDocumented annotation class LocalDateTimeValidatorConstraint( val message: String = "Invalid value", val groups: Array<KClass<*>> = [], val payload: Array<KClass<*>> = [] ) class LocalDateTimeValidator : ConstraintValidator<LocalDateTimeValidatorConstraint, LocalDateTimeDelegate> { override fun initialize(constraintAnnotation: LocalDateTimeValidatorConstraint) { } override fun isValid(value: LocalDateTimeDelegate, context: ConstraintValidatorContext): Boolean { val country = Locale.getDefault().country.trim() logger.info("Current country is {}", country) return when (country) { "", "NL", "US", "PT", "ES", "UK", "FR"-> true else -> false } } companion object { val logger: Logger = getLogger<LocalDateTimeValidator>() } }
  • 28. Unnamed classes Unnamed classes in Kotlin were a focal point in Java vs Kotlin discussions They have arrived in Java
  • 29. Main.java void main() { System.out.println("Hello, world!"); } Unnamed Classes Main.kt fun main(args: Array<String>) { runApplication<CarPartsDataStructureApplication>(*args) } JEP 445: Unnamed Classes and Instance Main Methods Friendly competition drives innovation, but also a lot of synergy
  • 30. What’s next? ● Better understanding of the Kotlin Language. ● Better understanding of the Spring Framework, Quarkus, Ktor and where, when and how to use them. ● Search the Kotlin documentation before searching for the seemingly unexplainable. ● Nothing is perfect and Kotlin also falls into that category and recognizing that, allow us to be better. ● Check if Kotlin or Java are the right languages for you and your team
  • 31. Questions and answers Q: Is the use of tailrec only intended to make sure we use readable code as for example with recursive functions and then use it iteratively when compiled to the bytecode? A: A quick answer is yes. The nuanced answer is that, while tailrec enables the compiler to apply the optimization only if the function is tail recursive, it is only with the application of TCO, that we can make sure to avoid StackOverflow exceptions, because with TCO we use only one StackFrame. It is TCO that does this and not exactly the application of tailrec.
  • 32. Questions? I am an inquisitive cat
  • 33. Resources ● Null Safety: https://kotlinlang.org/docs/null-safety.html ● Inline: https://kotlinlang.org/docs/inline-functions.html ● Tail Call Optimization: https://kotlinlang.org/docs/functions.html#tail-recursive-functions ● Annotation use-site targets: https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets ● Spring Validation via AOP : https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-validation.html ● https://github.com/Kotlin/KEEP/issues/402 ● https://openjdk.org/jeps/445
  • 34. Source code and slides ● https://github.com/jesperancinha/kotlin-mysteries
  • 35. About me ● Homepage - https://joaofilipesabinoesperancinha.nl ● LinkedIn - https://www.linkedin.com/in/joaoesperancinha/ ● YouTube - JESPROTECH - https://www.youtube.com/@jesprotech ● Bluesky - https://bsky.app/profile/jesperancinha.bsky.social ● Mastodon - https://masto.ai/@jesperancinha ● GitHub - https://github.com/jesperancinha ● Hackernoon - https://hackernoon.com/u/jesperancinha ● DevTO - https://dev.to/jofisaes ● Medium - https://medium.com/@jofisaes