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
Related
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
For example, how is this:
class Cat(name: String, val age: Int) {
def this() = this("Garfield", 20)
}
val someCat = new Cat
someCat.age
res0: Int = 20
Different from:
class Cat(name: String = "Garfield", val age: Int = 20)
val someCat = new Cat
someCat.age
res0: Int = 20
Note:
I have seen answers to other questions(e.g here) that discuss the differences between Java & Scala in the implementation for auxiliary constructors. But I am mostly trying to understand why do we need them in Scala, in the first place.
Auxiliary constructors are good for more than just supplying defaults. For example, here's one that can take arguments of different types:
class MyBigInt(x: Int) {
def this(s: String) = this(s.toInt)
}
You can also hide the main constructor if it contains implementation details:
class MyBigInt private(private val data: List[Byte]) {
def this(n: Int) = this(...)
}
This allows you to have data clearly be the backing structure for your class while avoiding cluttering your class with the arguments to one of your auxiliary constructors.
Another use for auxiliary constructors could be migrating Java code to Scala (or refactoring to change a backing type, as in the example above) without breaking dependencies. In general though, it is often better to use a custom apply method in the companion object, as they are more flexible.
A recurring use case I noticed is, as Brian McCutchon already mentioned in his answer "For example, here's one that can take arguments of different types", parameters of Option type in the primary constructor. For example:
class Person(val firstName:String, val middleName:Option[String], val lastName: String)
To create a new instance you need to do:
val person = new Person("Guido", Some("van"), "Rossum")
But with an auxiliary constructor, the whole process will be very pleasant.
class Person(val firstName:String, val middleName:Option[String], val lastName: String){
def this(firstName:String, middleName:String, lastName: String) = this(firstName, Some(middleName), lastName)
}
val person = new Person("Guido", "van", "Rossum")
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"))
I know this is going against the very nature of Scala pureconfig ... however ...
Is it even feasible to implement with scala pureconfig configuration reading for this case class, so that instead of having strongly typed value( as String) for the constructor parameter "variable" to have Any type or at least String, Integer, Double, Array[Strings], Array[Integer], Array[Double].
case class Filter(
field: String,
operator: String,
variable: String // should support Int , Double , List[String], List[Int]
)
To my poor understanding, neither CoProductHint nor Custom Reader approach will work ...
By default pureconfig doesn't provide a way to read Any. If for a specific class you would like to read Any then you can define a codec for Any in the context of that class:
case class Filter(field: String, operator: String, variable: Any)
implicit val readFilter = {
implicit val readAny = new ConfigReader[Any] {
def from(config: ConfigValue): Either[ConfigReaderFailures, Any] = {
Right(config.unwrapped())
}
}
ConfigReader[Filter]
}
and then you can read Filter
val config = ConfigFactory.parseString(
"""
{
field: "foo"
operator: "bar"
variable: []
}
""")
println(pureconfig.loadConfig[Filter](config))
// will print Right(Filter(foo,bar,[]))
unwrapped converts a ConfigValue to Any recursively.
So the answer is yes, it if possible to tell pureconfig how to read Any.
The reason why pureconfig doesn't provide the codec for Any by default is because Any is the ancestor of all the classes in Scala and it's impossible to create a codec for anything (e.g. database connections). When you know that you are expecting a restricted set of types, like the ones you listed, you can wrap everything in a coproduct:
sealed abstract class MySupportedType
final case class MyInt(value: Int) extends MySupportedType
final case class MyDouble(value: Double) extends MySupportedType
final case class MyListOfString(value: List[String]) extends MySupportedType
final case class MyListOfInt(value: List[Int]) extends MySupportedType
final case class Filter2(field: String, operator: String, variable: MySupportedType)
and then use the default way to extract the coproduct value or a custom codec for MySupportedType
val config = ConfigFactory.parseString(
"""
{
field: "foo"
operator: "bar"
variable: {
type: mylistofint
value: []
}
}
""")
println(pureconfig.loadConfig[Filter2](config))
// will print Right(Filter2(foo,bar,MyListOfInt(List())))
Using a coproduct instead of Any limits the possible values that variable can have and let the compiler help you if something is wrong with what you are doing.
You can make that field ANY:
Example:
scala> case class Filter(
| field: String,
| operator: String,
| variable: Any // should support Int , Double , List[String], List[Int]
| )
defined class Filter
scala> Filter("anurag","data",List(12,34))
res5: Filter = Filter(anurag,data,List(12, 34))
scala> Filter("anurag","data",List(12.12,34.12))
res6: Filter = Filter(anurag,data,List(12.12, 34.12))
scala> Filter("anurag","data",List("Hello","Hii"))
res8: Filter = Filter(anurag,data,List(Hello, Hii))
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")))