-
Notifications
You must be signed in to change notification settings - Fork 0
lessons
Miguel Gamboa edited this page Apr 9, 2024
·
41 revisions
Lessons:
- 26-02-2024 - Lesson 01 - Introduction and Modern VMs, e.g. JVM, Node
- 26-02-2024 - Lesson 02 - Metadata, Classpath and Java Type System introduction
- 27-02-2024 - Lesson 03 - Members and static vs instance (non-static)
- 04-03-2024 - Lesson 04 - Boxing and Unboxing
- 04-03-2024 - Lesson 05 - Nested, Inner, Abstract classes, Virtual Methods
- 05-03-2024 - Lesson 06 - Anonymous Types
- 11-03-2024 - Lesson 07 - Kotlin to Java type system
- 11-03-2024 - Lesson 08 - Object declarations
- 12-03-2024 - Lesson 09 - Kotlin Reflection
- 18-03-2024 - Lesson 10 - KClass, KType and classifier
- 18-03-2024 - Lesson 11 - NaiveMapper
- 19-03-2024 - Lesson 12 - Annotations
- 26-03-2024 - Lesson 13 - Lab 01 - Trab 1
- 26-03-2024 - Lesson 14 - Lab 02 - Trab 2
- 27-03-2024 - Lesson 15 - Reflect Generics
- 08-04-2024 - Lesson 16 - Lab 01 - Trab 3
- 08-04-2024 - Lesson 17 - Lab 02 - Trab 4
- 09-04-2023 - Lesson 18 - Reflection Exercises
- Bibliography and Lecturing methodology: github and slack.
- Tools:
javac
,javap
,kotlinc
, JDK 17 andgradle
. - Program outline in 3 parts:
- Java Type System and Reflection;
- Metaprogramming and Performance;
- Lazy processing.
- Project in 3 parts according to program outline.
- Managed Runtime or Execution Environment or informally virtual machine (VM) or runtime.
-
Execution Environment includes:
- Compiler,
- Programming languages,
- Standard libraries,
- Dependency manager (e.g. gradle, maven)
- Central Repository (e.g. Maven Central Repository, Nuget, NPM, etc)
- Examples of languages targeting JVM: Java, kotlin, Scala, Clojure.
- Examples of languages targeting Node: JavaScript, TypeScript, Kotlin.
- JVM runs
.class
files with bytecode - JVM translates bytecode to machine code (e.g. IA-32, AMD64, etc depending of the CPU)
- In Javascript ecosystem modules are deployed in source code i.e. Javascript.
- Distinguish between Programming Language
<versus>
VM
- One file
.class
for each class definition -
Metadata:
- data that provides information about other data
- In a
.class
the metadata provides a description and structure of a Type.
- Using javap -c -p AppKt.class to inspect metadata and bytecode definition of class
AppKt
- CLASSPATH
- e.g.
-cp . - local folder
- e.g.
-cp '.:~/JetBrains/IntelliJIdea2022.2/plugins/Kotlin/lib/*'
- (for windows use ; rather than :)
- e.g.
- Type System - Set of rules and principles that specify how types are defined and behave.
- Two kinds of types: Primitive and Reference types.
- Classes have Members
- Members may be: Fields or Methods.
- There are NO properties in Java Type System.
- Using
javap -p Person.class
to inspect metadata - The fully qualified name of a class includes its package name.
-
Constructor is a method with the name
<init>
returning void.
- Member access syntax:
Receiver.Member
. - The Receiver is the target of the member access and it is a Type (for static members) or an Object (for non-static/instance members).
- JOL - Java Object Layout:
java -cp .;jol-cli-0.17-full.jar org.openjdk.jol.Main estimates <classqualifiedname>
- (linux replace
;
by:
on-cp
(classpath))
- Object header = mark word (used for hash, locks, GC, etc) + class word (class-specific metadata).
- Fields alignment, reorder and padding gap.
- Static initializer: initializes static fields.
-
Immutable values:
-
constant value calculated at compile time, e.g. Kotlin
const
- immutable, yet dynamically initializable
-
constant value calculated at compile time, e.g. Kotlin
- Boxing and Unboxing.
-
Boxing :
<Wrapper>.valueOf(value)
- Heap allocation
- Object header initialization
- Copy value
-
Unboxing:
<Wrapper Instance>.<primitive>Value()
, e.g.nr.intValue()
- Nested classes.
-
Inner classes: non-static in Java or
inner
in Kotlin. - Abstract classes cannot be directly instantiated.
- Interfaces represent abstract types that cannot be instantiated too.
- Override
- Names collision and Member access ambiguity
- Methods call resolution: Fields, Methods, and Virtual Methods.
- Anonymous Classes in Java
- Object Expressions in Kotlin
- Resulting types from compilation
- Homework 2 - virtual and non-virtual methods.
- Kotlin Class Members
- Analyzing Kotin properties in JVM.
- There are NO properties in Java Type System.
- A Kotlin property may generate:
- Backing field
- Getter, i.e. function
get...
-- called withinvoke...
bytecode. - Setter, i.e. function
set...
(if defined withvar
).
- Top-level declarations
- Extension functions
- Singleton design pattern
-
object
keyword:- private constructor
- Singleton instance in static INSTANCE field;
- companion object - specific type of object declaration associated with its owner class.
- Function Types
- Kotlin compiler generates an _anonymous class: that implements the interface aligned with the respective function type.
- Reflection object oriented API for metadata
-
Reflection
--->
metadata--->
Type System - Type System: types have members
-
Kotlin Reflection API:
KClass
----->*
KCallable
- An instance of
KClass
may represent a type in Kotlin. - An instance of
KCallable
may represent a member in Kotlin.
- An instance of
-
KCallable
base type ofKFunction
andKProperty
-
KProperty
andKMutableProperty
-
KCallable
----->*
KParameter
-
KFunction
properties:name
,type
,parameters
andinstanceParameter
. -
KParameter
propertykind
:INSTANCE
versusEXTENSION_RECEIVER
-
KParameter
propertyisOptional
-
KClass
::createInstance()
-
KFunction
::call()
- To directly reference a
KType
, we use thetypeOf
function:- e.g.
func.returnType != typeOf<Unit>()
- e.g.
-
KType
holds information about nullability and type arguments. -
KType
properties:isMarkedNullable
,arguments
, andclassifier
.-
arguments
provide information about the type arguments (i.e.List<KType>
) -
classifier
provides a reference to the associated class (i.e.KClassifier
).-
KClassifier
is the base type ofKClass
-
-
- Implement an utility extension
Appendable.log(obj:Any)
-
isAccessible
- Provides a way to suppress JVM access checks for a callable. - Test with Kotlin domain classes and Java domain classes
-
Appendable.logGetters(obj:Any)
in Kotlin to inspect Java getters:- methods with prefix
get
- a single argument corresponding to the instance parameter
- return type different from
Unit
: - e.g.
m.returnType.classifier != Unit::class
- methods with prefix
-
NaiveMapper
, takes inspiration from libraries likeAutoMapper
orMapStruct
:- Simplify the process of mapping data between objects of different types by copying values from properties of one object to corresponding properties of another object.
- Offers an extension function like
Any.mapTo(dest: KClass<*>): Any
- 1st version - through mutable properties (i.e.
KMutableProperty
):- The destination type must have a parameterless constructor
- The source and destination properties share the same name and type
- The destination properties are mutable
- 2nd version - through constructor parameters:
- Call constructor via:
fun call(vararg args: Any?): R
- Call constructor via:
- 3rd version - avoid Reflect on mapping function:
-
NaiveMapper<T : Any>(val srcKlass: KClass<*>, val destKlass: KClass<T>)
:- Look for matching properties once in initalization
-
fun mapFrom(source: Any): T
- instantiatedestKlass
with values fromsource
-
- Annotations in the JVM are a form of metadata that can be added to Java classes, methods, fields, and other program elements.
- Annotations are strongly typed
- Each annotation inherits from
java.lang.annotation.Annotation
- E.g. JUnit annotation
@Test
corresponds to the following type:
public interface org.junit.Test extends java.lang.annotation.Annotation{...}
- Kotlin Reflect API on annotations:
annotations: List<Annotation>
findAnnotation<T>(): T
hasAnnotation<T>(): Bool
- When a Kotlin member generates multiple Java members, there are multiple potential locations.
- Use site target to explicitly specify the destination location within the metadata:
- e.g.
@property:MapProp("from") val country: String
- e.g.
- Specify the allowed elements with the
@Target
annotation. - Enhance
NaiveMapper
to map properties to parameters with different name through the annotation@MapProp
-
NaiveMapper
recursive mapping complex type properties with auxiliaryNaiveMapper
instances. - Mapping instances of
List
- E.g. from a property of type
List<Song>
-
prop.returnType.classifier
isList::class
-
prop.returnType.arguments[0].type
istypeOf<Song>
-
prop.returnType.arguments[0].type.classifier
isSong::class
-
fun KClass<*>.isSubclassOf(base: KClass<*>): Boolean
fun KClass<*>.isSuperclassOf(derived: KClass<*>): Boolean
- You have to develop a
ComparerOrder<T : Any>(klass: KClass<T>)
that implementsComparator
and it is able to compare instances of type represented byklass
, according to the properties which are both:Comparable
and annotated withComparison
. Notice that you should compare respecting the order specified in annotation. Example:
class Student (
@Comparison(2) val nr:Int,
val name: String,
@Comparison(1) val nationality: String,
) |
val s1 = Student(12000, "Ana", "pt")
val s2 = Student(14000, "Ana", "pt")
val s3 = Student(11000, "Ana", "en")
val cmp = ComparerOrder(Student::class)
assertTrue { cmp.compare(s1, s2) < 0 } // same nationality and 12000 is < 14000
assertTrue { cmp.compare(s2, s3) > 0 } // “pt” is > “en” |
- You have to develop a
Comparer<T : Any>(klass: KClass<T>)
that implementsComparator
and it is able to compare instances of type represented byklass
, according to the properties which are:Comparable
OR annotated with aComparison
that specifies aComparator
for that property, according to the following example:
class Person(
val id: Int,
val name: String,
@Comparison(cmp = AddressByRoad::class) val address: Address,
@Comparison(cmp = AccountByBalance::class) val account: Account) {
}
class AccountByBalance : Comparator<Account>{
override fun compare(o1: Account, o2: Account): Int {
return o1.balance.compareTo(o2.balance);
}
}
class AddressByRoad : Comparator<Address> {
override fun compare(o1: Address, o2: Address): Int {
return o1.road.compareTo(o2.road)
}
} |
val p1 = Person(11000, "Ana", Address("Rua Amarela", 24), Account("FD3R", 9900))
val p2 = Person(11000, "Ana", Address("Rua Rosa", 24), Account("8YH5", 9900))
val p3 = Person(11000, "Ana", Address("Rua Rosa", 24), Account("JK2E", 100))
val p4 = Person(11000, "Ana", Address("Rua Rosa", 97), Account("BFR5", 100))
val p5 = Person(17000, "Ana", Address("Rua Rosa", 97), Account("BFR5", 100))
val cmp = Comparer<Person>(Person::class)
assertTrue { cmp.compare(p1, p2) < 0 } // Rua Amarela is < Rua Rosa
assertTrue { cmp.compare(p2, p3) > 0 } // 9900 is > 100
assertEquals(0, cmp.compare(p3, p4)) // All properties are equal
assertTrue { cmp.compare(p4, p5) < 0 } // 11000 is < 17000 |