I have defined the following classes in Scala:
case class Info {
name: String,
age: Int,
dept: String
}
case class Foo {
id: String,
info: Info
}
Is there a way to obtain automatically the following class from Foo and Info:
case class FlatFoo {
id: Option[String],
name: Option[String],
age: Option[Int].
dept: Option[String]
}
This looks like a task for a very hardcore macro.
But I wonder if you just want to shorten your field accessors like
obj.fieldX.fieldY.fieldZ....
In that case you might be interested in concept named Lens. Which is beautifully implemented in monocle library
Consider such definition of your case classes:
import monocle.macros.Lenses
#Lenses
case class Info(name: String,
age: Int,
depts: List[String]
)
#Lenses
case class User(id: String,
info: Info
)
That #Lenses attribute generates special functional getter&setter for each field . Those lenses are located implicitly in companion object. But you could add your own, composing existent.
import monocle.function.Index._
import monocle.std.list._
object User {
val name = info ^|-> Info.name
val age = info ^|-> Info.age
val mainDept = info ^|-> Info.depts ^|-? index(0)
}
Now having
val oleg = User("odomontois", Info("Oleg", 23, List("python", "ABAP")))
You can see that
User.mainDept.getOption(oleg) == Some("python")
(User.age.set(28) andThen User.mainDept.set("scala")) (oleg) ==
User("odomontois",Info("Oleg",28,List("scala", "ABAP")))
Related
I have 2 case classes in Scala, like this:
case class CaseClass(
commonAttribute1: Int,
commonAttribute2: String,
commonAttribute3: String,
commonAttribute4: String,
commonAttribute5: String,
.
.
.
)
case class CaseClassDuplicate(
commonAttribute1: Int,
commonAttribute2: String,
commonAttribute3: String,
commonAttribute4: String,
commonAttribute5: String,
.
.
.
particularAttribute1: Timestamp,
particularAttribute2: Long
)
Is there a way to create a common class so that when you declare the duplicate class to write something like this: CaseClassDuplicate(particularAttribute1: Timestamp,particularAttribute2: Long), instead of re-writing all of the members? Thanks!
You can try the following two solutions:
case class CaseClassDuplicate(
common: CaseClass,
particularAttribute1: Timestamp,
particularAttribute2: Long)
or
trait AbstractCommonClass {
val commonAttribute1: Int
val commonAttribute2: String
val commonAttribute3: String
val commonAttribute4: String
val commonAttribute5: String
}
trait AbstractPractical {
val particularAttribute1: Timestamp
val particularAttribute2: Long
}
case class ConcreteClass() extends AbstractCommonClass with AbstractPractical
And implement all variables on ConcreteClass.
Its all depends on your usage, share more details to get more specific answer
I need to create an instance of a class using an array of values.
I know how much parameters the class have
All class parameters are string
I tried:
class Person(val name: String, val lastName: String)
{
}
fun main()
{
val values= listOf<String>("James", "Smith")
val myPerson = Person(values);
}
Is possibly do something like that?
You could create a custom constructor that takes a list and uses it to instantiate your class:
class Person(val name: String, val lastName: String) {
constructor(values: List<String>) : this(values[0], values[1])
}
However, I would say you should avoid this since it is very error-prone (what if the provided values list is empty or have only one element?).
case class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address)
class Details(val age: Int, val address: String)
val person = Person("Alex", 33, "Europe")
val details = person.asInstanceOf[Details] // ???
println(details) // I want only Details class fields
I have these 2 classes. In reality, both have a lot of fields. Somewhere, I need only field of superclass, taken from Person class.
There is a nice way to get only super class values and not mapping them field by field?
*I'm pretty sure I'll have some problems with json writes for class Details (which is not a case class and have not a singleton object, but this is another subject)
If I get your question correctly, then you might be asking me runtime polymorphism or dynamic method dispatch from java.
If so, you may have to create both the class and not case class
class Details( val age: Int, val address: String)
class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address) {
}
Now create the object of person and reference to superclass (Details)
val detail:Details = new Person("Alex", 33, "Europe")
println(detail.address)
println(detail.age)
This way you will be able to get the only address and age
Another way is like , why can't we create the Details a separate entity like:
case class Details( age: Int, address: String)
case class Person(name: String,
details: Details
)
val detail = Person("Alex", Details(10,"Europe") )
Output:
println(detail.details)
Details(10,Europe)
I will post a solution that leverages scala macro system (old kind, not the newest introduced with Scala 3.0). It could be an overkill for you...
BTW, if you want to access to only parent values (for example for getting key, value pair), you can:
given a type tag, get all parents;
from them, extract all the accessors (vals);
for each val, get its value;
and finally returns a list with all accessors taken
So, I try to solve each point step by step.
First of all, we have to write the macro definition as:
object Macros {
def accessors[T](element : T): String = macro MacrosImpl.accessors[T]
}
object MacrosImpl {
def accessors[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[String] = ...
}
for the first point, we can leverage the reflection macroprogramming API using c.universe:
import c.universe._
val weakType = weakTypeTag[T] //thanks to the WeakTypeTag typeclass
val parents = weakType.tpe.baseClasses
for the second point, we can iterate over the parent classes and then take only the public accessors:
val accessors = parents
.map(weakType.tpe.baseType(_))
.flatMap(_.members)
.filter(_.isPublic)
.filter(_.isMethod)
.map(_.asMethod)
.filter(_.isAccessor)
.toSet
So, for example, if the we write Macros.accessors[Details](person), accessors will yield age and address.
To take the value, we can leverage quasiqouting. So, first we take only the values name:
val names = accessors
.map(_.fullName)
.map(_.split("\\."))
.map(_.reverse.head)
Then we convert them into a TermName:
val terms = names.map(TermName(_))
And finally, we convert each term to a key value tuple containing the val name and its value:
val accessorValues = terms
.map(name => c.Expr[(String, Any)](q"(${name.toString}, ${element}.${name})"))
.toSeq
The last step consist in convert a Seq[Expr[(String, Any)] into a Expr[Seq[(String, Any)]. A way to do that, could be leveraging recursion, reify, and splicing expression:
def seqToExprs(seq: Seq[Expr[(String, Any)]]): c.Expr[Seq[(String, Any)]] =
seq.headOption match {
case Some(head) =>
c.universe.reify(
Seq((head.splice._1, head.splice._2)) ++
seqToExprs(seq.tail).splice
)
case _ => c.Expr[Seq[(String, Any)]](q"Seq.empty")
}
So now I decide to return a String representation (but you can manipulate it as you want):
val elements = seqToExprs(accessorValues)
c.Expr[String](q"${elements}.mkString")
You can use it as:
import Macros._
class A(val a : Int)
class B(val b : Int) extends A(b)
class C(val c: Int) extends B(c)
//println(typeToString[List[Set[List[Double]]]])
val c = new C(10)
println(accessors[C](c)) // prints (a, 10)(b, 10)(c, 10)
println(accessors[B](c)) // prints (a, 10)(b, 10)
println(accessors[A](c)) // prints (a, 10)
And, using your example:
// Your example:
case class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address)
class Details(val age: Int, val address: String)
val person = Person("Alex", 33, "Europe")
println(accessors[Details](person)) // prints (address,Europe)(age,33)
println(accessors[Person](person)) // prints (address,Europe)(age,33)(name,Alex)
Here there is a repository with the macro implemented.
Scala 3.0 introduce a safer and cleaner macro system, if you use it and you want to go further you can read these articles:
macros tips and tricks
short tutorial
another tutorial
I have defined a simple scala annoation and use it to annotate my case class Person:
package com.my
import scala.annotation.StaticAnnotation
#scala.annotation.meta.field
class Description(value: String) extends StaticAnnotation{
}
Then I use it in my Person class:
package com.my
import scala.beans.BeanProperty
case class Person(
#Description(value = "name00")
name: String,
#Description(value = "age00")
age: Int,
#BeanProperty
xyz: String = "xyz"
)
object Person {
def main(args: Array[String]): Unit = {
val p = Person("abc", 21)
classOf[Person].getDeclaredFields.foreach {
field =>
field.setAccessible(true)
val name = field.getName
val value = field.get(p)
//annotations always return empty array
val annotations = field.getDeclaredAnnotations
annotations.foreach {
annotation =>
val tpe = annotation.annotationType()
println(tpe)
}
println(s"name is $name, value is: $value")
}
}
}
In the main method of Person object, the annotations array is always empty,
I would like to ask how to get the annoation information defined on the field.
Firstly, annotations written in Scala are accessible in sources (if the annotations extend scala.annotation.Annotation) and class files (if if the annotations extend scala.annotation.StaticAnnotation). In order to be accessible at runtime the annotations must be written in Java
import java.lang.annotation.*;
#Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Description {
String value();
}
How to use Scala annotations in Java code
Why annotations written in Scala are not accessible at runtime?
https://www.reddit.com/r/scala/comments/81qzs2/how_to_write_annotations_in_scala/
Alternatively you can use the original #Description (written in Scala and extending StaticAnnotation) but then you have to access it at runtime via Scala reflection rather than Java reflection.
Secondly, you misuse meta-annotations (#scala.annotation.meta.field). They should annotate not the definition of #Description but its applications.
import scala.annotation.meta.field
case class Person(
#(Description #field)(value = "name00")
name: String,
#(Description #field)(value = "age00")
age: Int,
#BeanProperty
xyz: String = "xyz"
)
Annotating case class parameters
How can I reflect on a field annotation (Java) in a Scala program?
Calling a method from Annotation using reflection
Output:
interface Description // appears
name is name, value is: abc
interface Description // appears
name is age, value is: 21
name is xyz, value is: xyz
With Scala reflection you can do
import scala.annotation.StaticAnnotation
import scala.beans.BeanProperty
import scala.annotation.meta.field
import scala.reflect.runtime.universe._
class Description(value: String) extends StaticAnnotation
case class Person(
#(Description #field)(value = "name00")
name: String,
#(Description #field)(value = "age00")
age: Int,
#BeanProperty
xyz: String = "xyz"
)
def main(args: Array[String]): Unit = {
val p = Person("abc", 21)
typeOf[Person].decls
.collect { case t: TermSymbol if t.isVal => t.annotations }
.foreach(println)
}
//List(Description #scala.annotation.meta.field("name00"))
//List(Description #scala.annotation.meta.field("age00"))
//List(scala.beans.BeanProperty)
I reviewed some code from a colleague and I came across a case class which is by default immutable.
the below case class can be changed so my question is how is this possible since case classes are immutable but in this construct i can change the case class parameters?
case class RegisterCustomerRequest(`first-name`: String,
`last-name`: String,
`house-details`: String,
street: String,
zipcode: String,
city: String
extends WcRequestData {
def this(cardHolderData: CardHolderData,
registrationCode: RegistrationCode,
customerNumber: Long,
cardDesignImageId: String) =
this(`first-name` = cardHolderData.firstname,
`last-name` = cardHolderData.lastname,
street = cardHolderData.streetAndNumber,
zipcode = cardHolderData.zipCode,
city = cardHolderData.city,
# `house-details` =
s"${if (cardHolderData.employerName.contains("&"))
cardHolderData.employerName.replace("&" , " & ") else " /
"}${cardHolderData.employerName} ")#
}
why can I define a def this method which can change the values of parameters. What is this construct good for is this good coding style?
The case class RegisterCustomerRequest is still immutable however it has an auxiliary constructor def this which allows it to be constructed in a different way. For example, given
case class User(name: String)
case class Foo(name: String) {
def this(user: User) {
this(name = user.name)
}
}
we can construct Foo like so
Foo("picard")
or using the auxiliary constructor
new Foo(User("picard"))
In both cases the result is an immutable object. To confirm immutability try reassigning name after construction
(new Foo(User("picard"))).name = "worf" // Error: reassignment to val
As suggested by som-snytt, we can define apply method on companion object instead of auxiliary constructor like so
object Foo {
def apply(user: User): Foo = Foo(user.name)
}
which enables the following construction
Foo(User("picard"))