Cześć,
Próbuję dogłębnie zrozumieć na poziomie kompilatora działanie implicit conversions w Scali 2 w użyciu w Type Classes, wykorzystując taki przykład z Programming Scala:
case class Address(street: String, city: String)
case class Person(name: String, address: Address)
trait ToJSON {
def toJSON(level: Int = 0): String
val INDENTATION = " "
def indentation(level: Int = 0): (String, String) = (INDENTATION * level, INDENTATION * (level + 1))
}
object JsonInstances {
implicit class AddressToJSON(address: Address) extends ToJSON {
def toJSON(level: Int = 0): String = {
val (outdent, indent) = indentation(level)
s"""{
|${indent}"street": "${address.street}",
|${indent}"city":"${address.city}"
|$outdent}""".stripMargin
}
}
implicit class PersonToJSON(person: Person) extends ToJSON {
def toJSON(level: Int = 0): String = {
val (outdent, indent) = indentation(level)
s"""{
|${indent}"name": "${person.name}",
|${indent}"address": ${person.address.toJSON(level + 1)}
|$outdent}""".stripMargin
}
}
}
object TypeClassApplication {
def main(args: Array[String]): Unit = {
val a = Address("1 Scala Lane", "Anytown")
val p = Person("Buck Trends", a)
println(a.toJSON())
println()
println(p.toJSON())
}
}
Output:
{
"street": "1 Scala Lane",
"city":"Anytown"
}
{
"name": "Buck Trends",
"address": {
"street": "1 Scala Lane",
"city":"Anytown"
}
}
W tym przykładzie chodzi o rozszerzenie case class Address i Person i dorzucenie metod konwertujących obiekt Address i Person na JSON zapisanego jako String.
Z tego co rozumiem, to implicit conversions działają w tym przypadku tak? (Poprawcie mnie, jeśli coś źle tu rozumiem)
- Kompilator widząc wywołanie metody toJSON() na obiekcie a.toJSON() widzi, że ta metoda zwraca String, więc szuka konwersji implicit w lokalnym scope lub od razu w globalnym.
- Dzięki importowi import JsonInstances._, kompilator widzi te dwie konwersje implicit jako klasy implicit i próbuje dopasować typy do tych metod.
- Widzi, że klasa implicit AddressToJSON ma pojedynczy parameter address: Address, czyli typ na którym jest wywołana metoda a.toJSON() zgadza się, oraz widzi też metodę toJSON(), że zwraca typ String, więc jest dopasowanie i wywołuje już ta konkretną implementację toJSON() w klasie implicit AddressToJSON.
- To samo dzieje się dla klasy Person i wywołania p.toJSON()
Pytanie jest takie:
- Sprawdziłem jak wygląda bytecode po skompilowaniu za pomocą narzedzia javap, ale widzę, że klasa Address nie ma w swoim bytecode konkretnej metody toJSON() z klasy AddressToJSON
- Co w takim razie dorzuca kompilator, że mogę wywołać metodę toJSON() na obiekcie case class Address czy Person? Czy obiekt case classy ma w takim przypadku adres do metody toJSON() na Metaspace i może wywołać te instrukcje? Chodzi mi bardziej o to co się dzieje na poziomie Java Heap / Metaspace.
Z góry dzięki!