0% found this document useful (0 votes)
4K views33 pages

Decoding Kotlin - Your Guide To Solving The Mysterious in Kotlin

These are the slides of the presentation I gave at the JetBrains HQ by the RAI in Amsterdam named "Decoding Kotlin - Your Guide to Solving the Mysterious in Kotlin". I want to express a big thank you to Xebia and JetBrains for the opportunity. I gave this presentation on the 24th of April 2024.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4K views33 pages

Decoding Kotlin - Your Guide To Solving The Mysterious in Kotlin

These are the slides of the presentation I gave at the JetBrains HQ by the RAI in Amsterdam named "Decoding Kotlin - Your Guide to Solving the Mysterious in Kotlin". I want to express a big thank you to Xebia and JetBrains for the opportunity. I gave this presentation on the 24th of April 2024.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 33

Decoding

Kotlin - Your
guide to
solving the
mysterious in
Kotlin
By João Esperancinha 2024/04/24
https://www.meetup.com/dutch-kotlin-user-group/events/
300229414/
Topics for today

Nullability Inline and cross-inline


1. Working with the Spring 1. The Java overview
Framework
2. Reflection to force nulls

Tail recursive => Tail Cal Data classes


Optimization (TCO) 1. Why things work and why
1. What is it things don't work
2. Why? 2. How to fix the ones that don't
3. How it makes us work 3. How to work with use-site
recursively and not use targets.
mutable
What does a `delegate` do?
and other use-site targets.
About me
João Esperancinha
● Java
● Kotlin
● Groovy
● Scala
● Software Engineer 10+ years
Kong Champion/Java Professional/Spring Professional
● JESPROTECH owner for 1 year
Kotlin promises a
guarantee of null-safety.
Although we can use
nullable members in our
classes, we really shouldn’t
whenever possible.

Nullability
Whenever
possible?
CRUD Entity Example

CREATE SEQUENCE car_parts_id_sequence


START WITH 1
INCREMENT BY 1
NO MINVALUE @Table(name = "CAR_PARTS")
NO MAXVALUE @Entity
CACHE 1; data class CarPart(
@Id
CREATE TABLE CAR_PARTS ( val id: Long,
id BIGINT NOT NULL DEFAULT val name: String,
nextval('car_parts_id_sequence'::regclass), val productionDate: Instant,
name VARCHAR(100), val expiryDate: Instant,
production_date timestamp, val barCode: Long,
expiry_date timestamp, val cost: BigDecimal
bar_code BIGINT, )
cost float
);
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
Is this fun `should mysteriously get a list with a
car part with a name null`() {
possible? carPartDao.findAll()
.filter { it.name == null }
.shouldHaveSize(1)
}
Reflection Example

val carPartDto = CarPartDto(


id = 123L,
Is this
name = "name",
productionDate = Instant.now(), possible?
expiryDate = Instant.now(),
cost = BigDecimal.TEN, data class CarPartDto(
barCode = 1234L val id: Long,
) val name: String,
println(carPartDto) val productionDate:
val field: Field = CarPartDto::class.java Instant,
.getDeclaredField("name") val expiryDate: Instant,
field.isAccessible = true val barCode: Long,
field.set(carPartDto, null) val cost: BigDecimal
println(carPartDto) )
assert(carPartDto.name == null)
println(carPartDto.name == null)
Inline and crossline can be
used in combination with
each other. Inline provides
bytecode copies of the
code per each call point
and they can even help
Inline and avoid type erasure.
Crossinline improves
crossinline. readability and some
safety, but nothing really
Why does this
functional.

matter?
Crossinline as just a marker

fun main() { public final class IsolatedCarPartsExampleKt {


callEngineCrossInline { public static final void main() {
println("Place key in ignition") int $i$f$callEngineCrossInline = false;
int var1 = false;
println("Turn key or press push button ignition") String var2 = "This is the start of the loop.";
println("Clutch to the floor") System.out.println(var2);
println("Set the first gear")
}.run { println(this) } introduction((Function0)IsolatedCarPartsExampleKt$ca
Decompil
} llEngineCrossInline$1$1.INSTANCE);
var2 = "This is the end of the loop."; ed
System.out.println(var2);
inline fun callEngineCrossInline(startManually: () -> Unit) {
run loop@{
String var4 = "Engine started!";
System.out.println(var4);
code
println("This is the start of the loop.") Unit var3 = Unit.INSTANCE;
introduction { int var5 = false;
println("Get computer in the backseat") System.out.println(var3);
return@introduction }
}
public static final void introduction(@NotNull
println("This is the end of the loop.")
} Function0 intro) {
println("Engine started!") Intrinsics.checkNotNullParameter(intro,
} "intro");
LocalDateTime var1 = LocalDateTime.now();
fun introduction(intro: () -> Unit) { System.out.println(var1);
println(LocalDateTime.now()) intro.invoke();
intro() } public final void invoke() {
return String var1 = "Get computer in the
} backseat";
System.out.println(var1);
}
Crossinline as just a marker

fun main() { public final class IsolatedCarPartsExampleKt {


callEngineCrossInline { public static final void main() {
println("Place key in ignition") int $i$f$callEngineCrossInline = false;
int var1 = false;
println("Turn key or press pus button ignition") String var2 = "This is the start of the loop.";
println("Clutch to the floor") System.out.println(var2);
println("Set the first gear")
}.run { println(this) }
introduction((Function0)(new
IsolatedCarPartsExampleKt$main$
Decompil
} $inlined$callEngineCrossInline$1()));
var2 = "This is the end of the loop."; ed
System.out.println(var2);
inline fun callEngineCrossInline(crossinline startManually: () -
> Unit) {
String var4 = "Engine started!";
System.out.println(var4);
code
run loop@{ Unit var3 = Unit.INSTANCE;
println("This is the start of the loop.") int var5 = false; public final void invoke() {
introduction { String var1 = "Get computer
System.out.println(var3); in the
println("Get computer in the backseat") } backseat";
startManually() System.out.println(var1);
return@introduction public static final void introduction(@NotNull
Function0 intro) { int var2 = false;
}
Intrinsics.checkNotNullParameter(intro, String var3 = "Place key in ignition";
println("This is the end of the loop.")
} "intro"); System.out.println(var3);
println("Engine started!") LocalDateTime var1 = LocalDateTime.now(); var3 = "Turn key or press push button
} System.out.println(var1); ignition";
intro.invoke(); System.out.println(var3);
fun introduction(intro: () -> Unit) { }
var3 = "Clutch to the floor";
println(LocalDateTime.now()) System.out.println(var3);
intro() var3 = "Set the first gear";
return
System.out.println(var3);
}
}
Crossinline for safety

object SpecialShopNonLocalReturn { object SpecialShopLocalReturn {


inline fun goToStore(crossinline block: () -> Unit) {
inline fun goToStore(chooseItems: () -> Unit) { println("Walks in")
println("Walks in") block()
chooseItems() }
}
@JvmStatic
@JvmStatic fun main(args: Array<String> = emptyArray()) {
fun main(args: Array<String> = emptyArray()) { goToStore {
goToStore { println("Make purchase")
println("Make purchase") return@goToStore
return@main }
} println("Walks out")
println("Never walks out") }
} }
}

@JvmStatic @JvmStatic
public static final void main(@NotNull String[] args) { public static final void main(@NotNull String[] args) {
Intrinsics.checkNotNullParameter(args, "args");
Intrinsics.checkNotNullParameter(args, "args");
SpecialShopLocalReturn this_$iv = INSTANCE;
SpecialShopNonLocalReturn this_$iv = INSTANCE; int $i$f$goToStore = false;
int $i$f$goToStore = false; String var3 = "Walks in";
String var3 = "Walks in"; System.out.println(var3);
System.out.println(var3); int var4 = false;
int var4 = false; String var5 = "Make purchase";
String var5 = "Make purchase"; System.out.println(var5);
String var6 = "Walks out";
System.out.println(var5);

?
System.out.println(var6);
} }

Decompil
ed
code
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
Tail Call code for better
performance.
Optimization
What is the
catch?
Tail Call Optimization

sealed interface Part {

Why use
val totalWeight: Double
}

sealed interface ComplexPart : Part{ tailrec fun totalWeight(parts: List<Part>, acc: Double =
}
val parts: List<Part>

data class CarPart(val name: String, val weight:


0.0): Double {
if (parts.isEmpty()) {
this?
Tail
Double) : Part {
override val totalWeight: Double
return acc
get() = weight }

val remainingParts = parts.drop(1) recursive


} val part = parts.first()
data class ComplexCarPart(
val name: String, val currentWeight = acc + part.totalWeight
val weight: Double, return when (part) {
override val parts: List<Part>
) : is ComplexPart -> totalWeight(remainingParts +
ComplexPart { part.parts, currentWeight)
override val totalWeight: Double
get() = weight
else -> totalWeight(remainingParts, currentWeight)
} }
}
data class Car(
val name: String,
override val parts: List<Part>
) : ComplexPart {

}
override val totalWeight: Double
get() = parts.sumOf { it.totalWeight } All
variables
are
Tail Call Optimization

tailrec fun totalWeight(parts: List<Part>, acc: Double =


public static final double totalWeight(@NotNull
0.0): Double { List parts, double acc) {
while(true) {
if (parts.isEmpty()) {
Intrinsics.checkNotNullParameter(parts, "parts");
if (parts.isEmpty()) {
return acc
return acc; }
} val part = parts.first()
val remainingParts = parts.drop(1)

return when (part) {


List remainingParts = CollectionsKt.drop((Iterable)parts,
double currentWeight = acc + part.getTotalWeight();
Variables
val currentWeight = acc + part.totalWeight
Part part = (Part)CollectionsKt.first(parts);
1);
is ComplexPart -> totalWeight(remainingParts +
if (part instanceof ComplexPart) {
part.parts, currentWeight) are mutable
List var10000 = CollectionsKt.plus((Collection)remainingParts,
else -> totalWeight(remainingParts,(Iterable)
currentWeight)

and
((ComplexPart)part).getParts());
}
acc = currentWeight;
parts = var10000;
}
} else {
acc = currentWeight;
parts = remainingParts;
algorithm is
}
}
iterative
}
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
Data classes
Why?
and
Frameworks
Working with Data classes

Doesn’t work Works!


Why
@Table(name = "CAR_PARTS")
@Entity
data class CarPart(
use- @Table(name = "CAR_PARTS")
@Entity
data class CarPart(
@Id
val id: Long,
site @Id
val id: Long,
@Column
@NotNull target @Column
@field:NotNull
@Size(min=3, max=20) @field:Size(min=3, max=20)
val name: String,
val productionDate: Instant,
s? val name: String,
val productionDate: Instant,
val expiryDate: Instant, val expiryDate: Instant,
val barCode: Long, val barCode: Long,
@Min(value = 5) @field:Min(value = 5)
val cost: BigDecimal val cost: BigDecimal
) )
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
Working with Data classes

@Target({ElementType.METHOD,
ElementType.FIELD,
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.PARAMETER,
ElementType.TYPE_USE})
PARAMETE
@Retention(RetentionPolicy.RUNTIME) R
@Repeatable(List.class)
@Documented is
@Constraint(
validatedBy = {} selected
)
public @interface Size {
Working with Data classes

public final class CarPart {


@Id
@NotNull
public final Instant getProductionDate() {
Not where we
private final long id; return this.productionDate; want them to
@Column
@NotNull
}
@NotNull
be,
private final String name; public final Instant getExpiryDate() { but where they
@NotNull return this.expiryDate;
private final Instant productionDate; }
are expected
@NotNull public final long getBarCode() {
private final Instant expiryDate; return this.barCode;
private final long barCode; }
@NotNull @NotNull
private final BigDecimal cost; public final BigDecimal getCost() {
public final long getId() { return this.cost;
return this.id; }
public CarPart(long id, @jakarta.validation.constraints.NotNull @Size(min =
} 3,max = 20) @NotNull String name, @NotNull Instant productionDate, @NotNull
@NotNull Instant expiryDate, long barCode, @Min(5L) @NotNull BigDecimal cost) {
public final String getName() { Intrinsics.checkNotNullParameter(name, "name");
return this.name; Intrinsics.checkNotNullParameter(productionDate, "productionDate");
} Intrinsics.checkNotNullParameter(expiryDate, "expiryDate");
Intrinsics.checkNotNullParameter(cost, "cost");
Working with Data classes

public final class CarPart {


@Id
private final long id;
@Table(name = "CAR_PARTS")
@Entity @Column
data class CarPart( @NotNull
@Id @Size(
val id: Long, min = 3,
@Column max = 20
@field:NotNull )
Since @field
@field:Size(min=3, @org.jetbrains.annotations.NotNull
max=20) forces the
val name: String, private final String name;
val productionDate:@org.jetbrains.annotations.NotNull
Instant,
target, these
val expiryDate: Instant,
private final Instant productionDate; annotations get
val barCode: Long, @org.jetbrains.annotations.NotNull
@field:Min(value = private
5)
applied where
final Instant expiryDate;
val cost: BigDecimal
private final long barCode;
they should
)
@Min(5L)
@org.jetbrains.annotations.NotNull
private final BigDecimal cost;
Delegation is a great part
of the Kotlin language and
it is quite different than
what we are used to seeing
in Java

Delegates and But how can we


other use-site
use it?
targets
Working with Delegates

interface Horn { annotation class DelegateToWagonHorn


fun beep()
} annotation class DelegateToCarHorn

class CarHorn : Horn {


override fun beep() { Where
class SoundDelegate(private val initialHorn: is {this being
Horn)
operator fun getValue(thisRef: Any?, property: KProperty<*>): Horn {
println("beep!")
return initialHorn
applied to?
}
} } Horn or
} SoundDelegate?
class WagonHorn : Horn {
override fun beep() {
class HornPack {
println("bwooooooo!")
@delegate:DelegateToWagonHorn
}
val wagonHorn: Horn by SoundDelegate(WagonHorn())
}

@delegate:DelegateToCarHorn
val carHorn: Horn by SoundDelegate(CarHorn())
}
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
SoundDelegate
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]); No Horn!
}
}
Working with Delegates

class SanitizedName(var name: String?) {


operator fun getValue(thisRef: Any?,
property: KProperty<*>): String? = name public final class PartNameDto {
static final KProperty[] $$delegatedProperties = new KProperty[]{Reflection.mutableProperty1(new
operator fun setValue(thisRef: Any?,
MutablePropertyReference1Impl(PartNameDto.class, "name", "getName()Ljava/lang/String;", 0))};
property: KProperty<*>, v: String?) {
@Nullable
name = v?.trim() private final SanitizedName name$delegate = new SanitizedName((String)null);
} @NotBlank
} @Size(
max = 12
)
@Nullable
class PartNameDto {
public final String getName() {
@get:NotBlank
return this.name$delegate.getValue(this, $$delegatedProperties[0]);
@get:Size(max = 12) }
var name: String? by SanitizedName(null) …
override fun toString(): String {
return name ?: "N/A" public final class ImpossiblePartNameDto {
} static final KProperty[] $$delegatedProperties = new KProperty[]{Reflection.mutableProperty1(new
MutablePropertyReference1Impl(ImpossiblePartNameDto.class, "name", "getName()Ljava/lang/String;",
}
0))};
@NotBlank
class ImpossiblePartNameDto { @Size(
@delegate:NotBlank max = 12
@delegate:Size(max = 12) )
var name: String? by SanitizedName(null) @Nullable
override fun toString(): String { private final SanitizedName name$delegate = new SanitizedName((String)null);
@Nullable
return name ?: "N/A"
public final String getName() {
}
return this.name$delegate.getValue(this, $$delegatedProperties[0]);
} }
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]);
}
What’s next?
➔ Better understanding of the Kotlin Language.

➔ Don’t fight the Spring Framework or anything else like Quarkus. They are not evil and
they are not magic.

➔ Read the Kotlin documentation and only use Google as a last resort.

➔ Nothing is perfect and Kotlin also falls into that category and recognizing that, allow
us to be better.
Thank
you!
Questions?
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
Source code and slides
● https://github.com/jesperancinha/kotlin-mysteries

● Scribd:
https://www.scribd.com/presentation/726367924/Decoding-Kotlin-Your-Guide-to-Solving-the-Mysterious-in-Kotlin
● Slide-share:
https://www.slideshare.net/slideshow/decoding-kotlin-your-guide-to-solving-the-mysterious-in-kotlinpptx/267506251
About me
● Homepage - https://joaofilipesabinoesperancinha.nl
● LinkedIn - https://www.linkedin.com/in/joaoesperancinha/
● YouTube - JESPROTECH
■ https://www.youtube.com/channel/UCzS_JK7QsZ7ZH-zTc5kBX_g
■ 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
See you
Next
time!
See you
Next
time!

You might also like