Given the following class: case class Test(id: Long, name: String) {
val details = Map("size" -> 54)
}
How do i extract details without instantiating the class? I Know how to extract the id and the name using Scala reflection but can't figure out how to extract what i need.
There are no static members in Scala. Instead, when you have data that should be statically available from arbitrary code without instantiating the enclosing class, we usually define objects with that data.
object Test {
val Details = Map("size" -> 54)
}
case class Test(id: Long, name: String) {
val details = Test.Details
}
Note the case of the literal Details - unlike Java, there's a convention in Scala to name constants in CamelCase, starting with an uppercase letter.
Related
When I have a case class that has methods on vals, what exactly will get serialized?
case class User(id: Int, name: String, age: Int) {
val someNumber = age * 10
def someMethod(a: Int) = ....
}
So from the above I would image the constructor parameters and the val someNumber would get serialized, while the methods would not.
So basically the state of the method gets serialized.
Are there any big differences between scala and java serialization?
case class serialization in Scala is what we would expect from standard Java serialization.
case classes extend scala.Serializable, which in turn extend Any with java.io.Serializable (see scala.Serializable), so case classes are 'marked' serializable using the usual Java method of extending java.io.Serializable.
Note that we should speak of object serialization, not class serialization. What gets serialized is the state of the object instance and it consists of the values of all members, declared or inherited. In the case of bodyless case class, like
case class User (id:Long, name:String)
all declared members will be serialized (eg. id, name).
If the case class declares inner member variables, those will be also included in the serialized form.
case class User (id:Long, name:String) {
val foo = name.hashCode * id
}
the serializad form will include (id, name, foo).
We can mark members with the #transient annotation to avoid them being serialized.
case class User (id:Long, name:String) {
val foo = name.hashCode * id
#transient bar = "Private Bar."
}
the serializad form will include (id, name, foo).
Note that Java serialization always involve serializing the object graph consisting of all references attached to the object being serialized, so any object that's referenced by a member variable will also be serialized.
Like in this case:
case class User (id:Long, name:String)
case class Product (id:Long, name:String, price: Decimal)
case class Purchase (timestamp:Long, user:User, product:Product, name:String)
so, given:
val beerPurchase = Purchase(now, onlineUser, leffe)
Serializing beerPurchase will also involve the serialization of the onlineUser and leffe objects.
Note that every member of a serializable class must be also serializable or be marked #transient. Otherwise, attempting to serialize such class will result in a runtime java.io.NotSerializableException
In a nutshell: No surprises. case class serialization is what you would expect from standard java/jvm serialization.
Let's create an object and serialize it so you can easily see what's being serialized:
case class City(name: String, funActivity: String, latitude: Double)
val bengaluru = City("Bengaluru", "South Indian food", 12.97)
implicit val cityRW = upickle.default.macroRW[City]
upickle.default.write(bengaluru) // "{\"name\":\"Bengaluru\",\"funActivity\":\"South Indian food\",\"latitude\":12.97}"
The values encapsulated in the object are being serialized.
You can write your serializer to persist data in the object. You can't serialize any JVM stuff/Scala stuff. Serializing means converting the object to a string / binary. Arrays of bytes have no way to store JVM stuff like methods. See this post for more info about serializing Scala objects.
I'm pretty new to scala and struggling a bit with reflection.
Given the below class:
class Person (name: String) {
.....
}
and the given instance:
val p = new Person ("MyName")
How can i retrieve the value "MyName" in reflection?
BTW - I tried with java reflection using getClass.getDeclaredFields(), but without luck...
Thanks!
name is not a member of the class in your example, it's just a function parameter of the constructor. The difference in scala in rather subtle, because the whole class is defined inside the constructor body, so, it is, effectively, a closure, and you can use the parameters everywhere, but still, there is a difference.
class Person(val name: String) or class Person(var name: String) declares a class with an instance member (either final or writable) name.
class Person(name: String) has no instance variables, name is just a parameter to the constructor.
Case classes are special, as they treat all constructor parameters as vals by default, this is "syntactic sugar": case class Person(name: String) really means `case class Person(val name: String).
I think you should add modifier var or val when defining constructor.
i.e,
class Person (var name: String) {
.....
}
and to retrieve value "MyName", you can do,
val p= new Person("MyName");
and Name is: p.name
I have a Base class have some function and val, I want to inherent them in my inherent case class how to do it ?
This is my base class:
class Base(val name:String, val number:int) extends Sometrait {
def copy(name:String=this.name, number:int=this.number){
new Base(name, number)
}
}
I want to write the:
case class SomeCase(val name:String, val number:int, val id:int)extends Base(String, number){
...
}
But the compiler always told me:
value **** needs `override' modifier social.scala /scalatest/src/scalatest line 35 Scala
But I really want to is just do as inherent not override, how to do it.
(I need to put the child class as case class, as it is easy for me to use in slick. (here is my another question for how to use class as table content class in slick, someone give me really great answer, but I still mass.))
Because name and number fields in both Base and SomeCase are defined with val without any modifiers like private, they are both public members and participate in inheritance. Because these fields have same names in base and child classes, you have to add override modifier before val name and val number in the child class:
case class SomeCase(override val name: String,
override val number: Int,
val id: Int) extends Base(name, number) { ... }
I need to convert a string value into an actual type, so i decided to try a macro-way to do this. I have a bunch of data types:
sealed abstract class Tag(val name: String)
case object Case1 extends Tag("case1")
case object Case2 extends Tag("case2")
case object Case3 extends Tag("case3")
etc...
I want to write a simple resolver:
val tag: Tag = TagResolver.fromString("case2")
This line should return Case2 respectively. I manager to do the following:
def typeFromString(c: Context)(name: c.Expr[String]): c.Expr[Tag] = {
import c.universe._
val tag = typeTag[Tag]
val accSymb = tag.tpe.typeSymbol.asClass
val subclasses = accSymb.knownDirectSubclasses // all my cases
subclasses.map { sub =>
val name = sub.typeSignature.member(newTermName("name")).asMethod // name field
???
}
}
But how can i match name: c.Expr[String] against value of name field and if matched return the appropriate tag?
I don't think there's reliable way of doing this, because knownDirectSubclasses can refer to classes that haven't been compiled yet, so we can't evaluate them.
If you can put these values as annotations on the classes, then these annotations can be read even when classes are being compiled in the current compilation run (via the Symbol.annotations API). Please note, however, that knownDirectSubclasses has known issues: https://issues.scala-lang.org/browse/SI-7046.
Schema.org is markup vocabulary (for the web) and defines a number of types in terms of properties (no methods). I am currently trying to model parts of that schema in Scala as internal model classes to be used in conjunction with a document-oriented database (MongoDB) and a web framework.
As can be seen in the definition of LocalBusiness, schema.org uses multiple inheritance to also include properties from the "Place" type. So my question is: How would you model such a schema in Scala?
I have come up with two solutions so far. The first one use regular classes to model a single inheritance tree and uses traits to mixin those additional properties.
trait ThingA {
var name: String = ""
var url: String = ""
}
trait OrganizationA {
var email: String = ""
}
trait PlaceA {
var x: String = ""
var y: String = ""
}
trait LocalBusinessA {
var priceRange: String = ""
}
class OrganizationClassA extends ThingA with OrganizationA {}
class LocalBusinessClassA extends OrganizationClassA with PlaceA with LocalBusinessA {}
The second version tries to use case classes. However, since case class inheritance is deprecated, I cannot model the main hierarchy so easily.
trait ThingB {
val name: String
}
trait OrganizationB {
val email: String
}
trait PlaceB {
val x: String
val y: String
}
trait LocalBusinessB {
val priceRange: String
}
case class OrganizationClassB(val name: String, val email: String) extends ThingB with OrganizationB
case class LocalBusinessClassB(val name: String, val email: String, val x: String, val y: String, val priceRange: String) extends ThingB with OrganizationB with PlaceB with LocalBusinessB
Is there a better way to model this? I could use composition similar to
case class LocalBusinessClassC(val thing:ThingClass, val place: PlaceClass, ...)
but then of course, LocalBusiness cannot be used when a "Place" is expected, for example when I try to render something on Google Maps.
What works best for you depends greatly on how you want to map your objects to the underlying datastore.
Given the need for multiple inheritance, and approach that might be worth considering would be to just use traits. This gives you multiple inheritance with the least amount of code duplication or boilerplating.
trait Thing {
val name: String // required
val url: Option[String] = None // reasonable default
}
trait Organization extends Thing {
val email: Option[String] = None
}
trait Place extends Thing {
val x: String
val y: String
}
trait LocalBusiness extends Organization with Place {
val priceRange: String
}
Note that Organization extends Thing, as does Place, just as in schema.org.
To instantiate them, you create anonymous inner classes that specify the values of all attributes.
object UseIt extends App {
val home = new Place {
val name = "Home"
val x = "-86.586104"
val y = "34.730369"
}
val oz = new Place {
val name = "Oz"
val x = "151.206890"
val y = "-33.873651"
}
val paulis = new LocalBusiness {
val name = "Pauli's"
override val url = "http://www.paulisbarandgrill.com/"
val x = "-86.713660"
val y = "34.755092"
val priceRange = "$$$"
}
}
If any fields have a reasonable default value, you can specify the default value in the trait.
I left fields without value as empty strings, but it probably makes more sense to make optional fields of type Option[String], to better indicate that their value is not set. You liked using Option, so I'm using Option.
The downside of this approach is that the compiler generates an anonymous inner class every place you instantiate one of the traits. This could give you an explosion of .class files. More importantly, though, it means that different instances of the same trait will have different types.
Edit:
In regards to how you would use this to load objects from the database, that depends greatly on how you access your database. If you use an object mapper, you'll want to structure your model objects in the way that the mapper expects them to be structured. If this sort of trick works with your object mapper, I'll be surprised.
If you're writing your own data access layer, then you can simply use a DAO or repository pattern for data access, putting the logic to build the anonymous inner classes in there.
This is just one way to structure these objects. It's not even the best way, but it demonstrates the point.
trait Database {
// treats objects as simple key/value pairs
def findObject(id: String): Option[Map[String, String]]
}
class ThingRepo(db: Database) {
def findThing(id: String): Option[Thing] = {
// Note that in this way, malformed objects (i.e. missing name) simply
// return None. Logging or other responses for malformed objects is left
// as an exercise :-)
for {
fields <- db.findObject(id) // load object from database
name <- field.get("name") // extract required field
} yield {
new Thing {
val name = name
val url = field.get("url")
}
}
}
}
There's a bit more to it than that (how you identify objects, how you store them in the database, how you wire up repository, how you'll handle polymorphic queries, etc.). But this should be a good start.