I need to check if all value is null in a Scala class (Test.class) defined as:
class Test extends Serializable {
a: String = _
b: String = _
c: String = _
}
I can't change this class (is legacy code in my project).
How can i do this without a lot of if? The real class have 22 fields.
I've tried to use java reflection to detect the defined fields, but they are all defined every and I can't access it because the fields are private for the reflectors. (criteria.getClass.getDeclaredFields)
Thanks #Dmytro Mitin , it works for me. I think that I will use this approach for check the not null element:
testClass.getClass.getDeclaredFields.flatMap(f => {
f.setAccessible(true)
Option(f.get(criteria))
}).length
Related
Can I have Nullable parameters in Scala object.
Similar to Nullable in C# and the syntactic sugar:
public int? Age {get; set;}
public string Name {get; set;}
public bool? isAdmin {get; set;}
Can I create scala class where the object can have some of these attributes set while others not?
I intend to have multiple constructors for different purposes. I want to ensure a null value implies the field is not set.
UPDATE
for example I have the following case class
case class Employee(age: Int, salary: Int, scaleNum : Int,
name: String, memberId: Int )
I am using GSON to serialize the class in JSON.
In some cases however, I don't want to pass values for some of the parameters such that the GSON serializer will only include the parameters that have non-NULL value.
How can I achieve that with Options or otherwise?
A common declaration in Scala for your example would be:
case class Employee(
age: Option[Int], // For int? Age
name: String // For string Name
// your other decls ...
)
Then you can use the type easily:
val john = Employee( age = Some(10), name = "John" )
While Scala 2 allows null values for references types (like String, etc) it is slowly changing starting with Scala 3 (https://dotty.epfl.ch/docs/reference/other-new-features/explicit-nulls.html).
JSON support
Java libraries (like GSON) don't know anything about Scala, so you should consider using other libraries for JSON support that support Scala:
circe
play json
jsoniter-scala
uPickle
etc
Those libraries not just aware of Option[] in your class definitions, but also have improved support for Scala collections, implicits, default values and other Scala language features.
It is really important to choose an appropriate library for this, because with the Java JSON libs you will end up with Java-style classes and code, compatibility issues with other Scala libs.
With the Circe your example would be:
import io.circe._
import io.circe.syntax._
import io.circe.parser._
import io.circe.generic.auto._
val john = Employee( age = Some(10), name = "John" )
val johnAsJson = john.asJson.dropNullValues
decode[Employee]( johnAsJson ) match {
case Right(johnBack) => ??? // johnBack now is the same as john
case Left(err) => ??? // handle JSON parse exceptions
}
Null coalescing operator
Now you might be looking where is the Null Coalescing Operator (?? in C#, ? in Kotlin, ...) in Scala.
The direct answer - there is none in the language itself. In Scala we work with Option (and other monadic structures, or ADT) in FP way.
That means, for example:
case class Address(zip : Option[String])
case class Employee(
address: Option[Address]
)
val john = Employee( address = Some( Address( zip = Some("1111A") )) )
you should avoid this style:
if (john.address.isDefined) {
if(john.address.zip.isDefined) {
...
}
}
you can use map/flatMaps instead:
john.address.flatMap { address =>
address.zip.map { zip =>
// your zip and address value
??
}
}
// or if you need only zip in a short form:
john.address.flatMap(_.zip).map { zip =>
// your zip value
??
}
or for-comprehension syntax (which is based on the same flatMaps):
for {
address <- john.address
zip <- address.zip
}
yield {
// doing something with zip and address
??
}
The important part is that idiomatic way to solve this in Scala mostly based on patterns from FP.
I'm parsing configuration in scala case class with ficus library
Case class approximately looks like this
case class Entity(value: Any)
and configuration
{
value = "something"
}
value might be number or string.
But config.as[Entity]("pathToConfig") returns value as java.lang.Object with nothing.
How do I make ficus inject in value a Long or String depending on value in configuration?
I created a ValueReader for this particular Entity class like this
implicit val entityReader: ValueReader[Entity] = ValueReader.relative { config =>
config.getAnyRef("value") match {
case value: java.lang.Integer => Entity(value.toLong)
case value => Entity(value)
}
}
This way value inside Entity either String or Long
I'm very new to Scala and H2O. I know there is extend class in Scala. But what I want to do is have two classes with same name but different parameter type.
I hope when I call test, if I put either a String or a Double parameter into it. The class will recognize the Data type I put into it and run the right function. Thanks for your help!
You can parametrize your class to make it 'generic':
class test[T](a: T, b: T) extends MRTask {}
so when you'll use this class in real life type T will be determined automatically based on your input.
For test("str", "str" ) , T will be String
If you want to limitat your type with String and Double you can not use both this bounds in same time. You have to find common parent class for those two. It's Any. It will not help you a lot.
For ex. you need to implement some method that depends on type should apply different behaviour. For this purpose I recommend you to use pattern matching.
case class test[T <: Any](a :T, b: T) {
def foo (): T = {
a match {
case d: Double => d
case s: String => s
}
}
}
I'm trying to get the type of an attribute that refers to a custom class, I just get that it's of type Object
My code:
class Edge[N <% Node](var from : N, var to : N) {
def toXml(c: Class): xml.Elem = {
<edge>{
for(field: Field <- classOf[this.type].getDeclaredFields)
yield <field name={field.name} tpe={field.tpe.toString()}>{ this.getClass().getMethods.find(_.getName() == field.name).get.invoke(this) }</field>
}</edge>
}
So the problem here is that I need to switch between the java Field and scala Field: apparently there is no such thing as this.getClass in scala? So I need to go through Java to get the class?
However this seems to only result in Objects as types?
EDIT: The revised question is: Should scala.reflect.Field or java.lang.reflect.Field be used?
Answer: Always[*] use java.lang.reflect.Field, and in general java reflection for two reasons:
That is what is returned by xxx.getClass().getDeclaredFields()
The following comment is next to the definition of scala.reflect.Field
.
/** This type is required by the compiler and <b>should not be used in client code</b>. */
case class Field(override val fullname: String, tpe: Type) extends GlobalSymbol(fullname)
[*] At least for now. reflection is coming soon to Scala.
--
Original answer:
It would help if you posted the class code as well, but it seems that the field is declared as Object. getType returns the declaration class of the field.
From Field#getType():
Returns a Class object that identifies the declared type for the field
represented by this Field object.
class Foo {
var bar: String = "string"
var bar2: java.lang.Object = "string"
}
for (field <- new Foo().getClass.getDeclaredFields()) {
println("field=" + field.getName() + " " + field.getType.toString())
}
gives
field=bar class java.lang.String
field=bar2 class java.lang.Object
If you want the type of the instance, then you will have to do a .getClass() on the instance in the normal way.
Using Lift Record, when I try to retrieve the MongoDB entry below, a NullPointerException is raised when the .asHtml method of MongoCaseClassField is called.
object MyEnumeration extends Enumeration {
val A, B, C = Value
}
case class MyCaseClass(en: MyEnumeration.Value)
class MyRecord extends MongoRecord[MyRecord] with MongoId[MyRecord] {
def meta = MyRecord
object fail extends MongoCaseClassField[MyRecord, MyCaseClass](this)
}
object MyRecord extends MyRecord with MongoMetaRecord[MyRecord]
However this works fine if I use String instead of Enumeration. Is there any way to use enumerations in case class fields or should use a different type of field?
At the time of writing mongoDB doesn't place nice with scala enums, I use a decorator method as a work around.
Say you have this enum:
object EmployeeType extends Enumeration {
type EmployeeType = Value
val Manager, Worker = Value
}
and this mongodb record:
import EmployeeType._
case class Employee(
id: ObjectId = new ObjectId
)
In your mongoDB, store the integer index of the enum instead of the enum itself:
case class Employee(
id: ObjectId = new ObjectId,
employeeTypeIndex: Integer = 0
){
def employeeType = EmployeeType(employeeTypeIndex); /* getter */
def employeeType_=(v : EmployeeType ) = { employeeTypeIndex= v.id} /* setter */
}
The extra methods implement getters and setters for the employee type enum.
I am quite certain that this will only work with native types like String, Int, Float, Double, Boolean, etc, but not Enumerations. I am really certain that this is due to serialization.