Understand Scala immutable Map behaviour - scala

I used a scala immutable map as per below.
val d = "4.55"
This is working fine.
val properties = Map("title"->"title" , "value" -> d )
Its convert from [String , AnyRef] to [String, Any]
val properties = Map("title"->"title" , "value" -> d.toDouble )
Cant convert from Double to Object , runtime error
val properties:Map[String,Object] = Map("title"->"title" , "value" -> d.toDouble )
Why object cant accept the Double?
Working fine.
val properties:Map[String,Object] = Map("title"->"title" , "value" -> d.toDouble.asInstanceOf[Object] )
Cant understand the four scenario of Immutable Map behaviour.

Most important of all: Scala has no primitive types as Java do.
Scala's Double is a class, inherit from AnyVal, has it's own methods
But Java's Object is the base class of all reference types, aka...Class
So, what you did here is using Object as base class of Double.
In my opinion,
Scala's AnyRef is the corresponding type to Java's Object.
Scala's AnyVal is the corresponding type to Java's Primitive Types.

As you can see froom the Scala class hierarchy...
... the Scala equivalent of java.lang.Object is AnyRef, and type String is part of that family, but a Scala Double falls under AnyVal. When the compiler has to reconcile those two types it will find type Any, which can't be promoted to type AnyRef/Object without coercion (i.e. a cast).
If you had d.toSeq instead of d.toDouble the compiler would have gone to AnyRef/Object straight away.

Related

Why is String not a value?

scala> val a = Int
a: Int.type = object scala.Int
scala> val a = String
<console>:11: error: object java.lang.String is not a value
val a = String
^
Why didn't scala create a thin wrapper around java.lang.String in order to make String a value? Isn't it very restrictive when one of the main types (String) cannot be used in expressions or passed to / returned from functions?
Well... In Scala class's can have companion objects.
And that companion object for any class A is actually an instance of A.type.
So... when you do,
val i = Int
What you get is companion object of class Int which is an instance of Int.type
Keep in mind that Int is still not a value... this thing that you have is not Int. It is also not an instance of Int. It is an instance of Int.type.
But as far as String is concerned... it is defined as an Alias for the corresponding java.lang.String from the Java world and thus has no such companion object. And hence the val assignment does not work with it.
As for why String is not wrapped in Scala. There can be many reasons for this. But one main reason is because there was no need for that.
Scala wanted to be have pure object hierarchy such that everything is an object and inherits from the same root Any. And Java has basic-types like int, long etc... which are not objects... and this created a need to wrap them into Int.
Although the question probably turns on a misapprehension about reifying a type, the following suggests why you can select Java statics as though they were members of a companion object, even though there is no such object:
$ scala
Welcome to Scala 2.12.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_111).
Type in expressions for evaluation. Or try :help.
scala> java.lang.System.currentTimeMillis
res0: Long = 1483263161912
scala> java.lang.System
<console>:12: error: object java.lang.System is not a value
java.lang.System
^
scala> java.lang.System.toString
<console>:12: error: value toString is not a member of object System
Note that System extends Any, not AnyRef.
Such types can participate in value classes, but instances
cannot appear in singleton types or in reference comparisons.
java.lang.System.toString
^
scala>

Scala reflection with Int parameter

In the below code, I try to invoke an object's method that has an Int parameter (giving it a value of 3). This returns an error that Int and 3 are incompatible types.
//Using scala's Int does not work!
object MyObject{
def handleInt(id:Int) : Boolean = {
true
}
}
object testApp extends App {
val obj = MyObject.getClass
val method = obj.getDeclaredMethod("handleInt", classOf[Int]) //Int.getClass shows the same behavior
val rsp = method.invoke(obj, 3)
}
Error:(106, 41) the result type of an implicit conversion must be more
specific than AnyRef
val rsp = method.invoke(obj, 3)
Error:(106, 41) type mismatch; found : Int(3) required: Object
val rsp = method.invoke(obj, 3)
I tried modifying a lot of things here, the only way this could work is by changing all signatures to Java's Integer. The code will look like this:
//This works with Java's Integer
object MyObject{
def handleInt(id:Integer) : Boolean = {
true
}
}
object testApp extends App {
val obj = MyObject.getClass
val method = obj.getDeclaredMethod("handleInt", classOf[Integer])
val rsp = method.invoke(obj, 3)
}
My question(s) are:
Can someone explain why this happens? I think scala's Int wraps java's primitive int (which is why this is not considered an object), but I'm not sure.
Is there a way to achieve this using Scala's Int type?
Is it acceptable to mix scala and java types like this? Is it a good practice?
The first problem is that you invoke method on the wrong object: obj doesn't have handleInt method, MyObject does. The second is kind of an edge case: invoke takes Object... varargs and Scala doesn't automatically convert an Int directly to Object because that's not what you normally want to do. You can use a type ascription to say "box this Int into an Integer" and then Scala will upcast it to Object automatically.
So, combining these 2 fixes: you don't need to change handleInt or val method, just
val rsp = method.invoke(MyObject, 3: Integer)
To answer your final question: use of Integer, java.lang.Double, etc. shouldn't be common in Scala code, but it isn't particularly problematic. And reflection is one of the areas where you may have to use them.
No we cannot use Scala types.
Its Ok to mix Java and Scala types.
As reflection deals with reading class bytecode at Runtime. At bytecode level only types that are visible are the Java types because all scala types are boiled down to Java types.

scala.Null clarification

Want to clarify about scala.Null. As I know scala.Null has instance null and class. For example if write like this
val x: Null = null
Type inference set type for the x Null.
According Scala classes hierarhy "please see image" Scala cannot use variables without wrappers like Int, Double, etc. That is why we can found Null for the type inference mechanism resolving. Is it correct vision ?
Scala classes hierarhy
You seem to be mixing some of these concepts:
Type Inference is the ability of the compiler to infer the type when the code doesn't explicitly state it. In your example - you do state the type, and that type is Null - no inference here:
val x: Null = null
Here's a simple example of type inference:
scala> val whoKnows = 12
whoKnows: Int = 12
Compiler inferred the type Int since we assigned the value 12, which is an integer. There's much more to it (see http://docs.scala-lang.org/tutorials/tour/local-type-inference), but that's the basics.
null can be used in Scala just like in Java (although it's very much frowned upon, as Scala offers many safer alternatives, mainly None), for example:
scala> val nullStr: String = null
nullStr: String = null
scala> val nullList: List[Int] = null
nullList: List[Int] = null
Where can't null be used? Just like in Java - for "primitives" like int, double etc. In Scala, there's no such thing as a primitive, but there's still a difference between these "value" types (named with upper-case like other types, e.g. Int, Double) and other types: value types share the common trait AnyVal, whereas all other types share the trait AnyRef - to signify, among other things, this difference.
As for what the Null trait is good for - see Usages of Null / Nothing / Unit in Scala
The type Null in Scala is a special type. What's special about it is that it is a subtype of all reference types - it's a subtype of every type that is a subtype of AnyRef.
The reason for that is to make it possible to assign the value null to any reference type.
Note that besides reference types, Scala also has value types. These are the types that are subtypes of AnyVal. Examples of value types are the built-in types that map to the primitive types of the JVM: Byte, Short, Int, Long, Float, Double, Boolean, and Char, and also the type Unit.
Note that AnyVal and its subtypes are not subtypes of AnyRef. You can't assign null to a variable of a value type.
Note that the value types Int, Double etc. are not like Java's wrapper types (java.lang.Integer, java.lang.Double etc.). When you use the value types, there is no boxing and unboxing going on to/from objects. These value types directly map to the primitive types of the JVM. Therefore you cannot assign null to a variable of one of those types.
In other words: types like Int and Double are not wrappers, they directly represent primitive types.

bencode Scala Any type

I am porting over code that was written in Python over to Scala.
This python code [0,{}] creates a list of an integer and a dictionary.
The dictionary in this case will have keys that are strings, but the values can either be an int or a string.
I want to do something similar in Scala. I believe I would have to do something like:
List[Any] = (<int>, scala.collection.mutable.Map[String, Any](...))
I plan to bencode this list so the underlying type of 'Any' matters. How do I change the types so that the list can be bencoded?
For the most part, using Any is not the right thing to do in Scala.
Since your problem is that the map values can be either Int or String, rather than trying to use their common type (Any), make your own new type hierarchy to represent the possibilities:
sealed trait BData
case class BInt(i: Int) extends BData
case class BString(s: String) extends BData
Now your map will have a type signature of Map[String, BData]
Using BData as the "underlying type" is better than using Any because there is no ambiguity to it. It has exactly two subclasses; compared with Any, where your values might be a List[(Foo, Bar)], for all the compiler knows.
I'll take dylan answer little further.
Scala have strong type system and you should use it.
When you are using Any which is like Object in java, you lose the type.
You should define the types and let the compiler work for you:
sealed trait BData[T] {
val value: T
}
implicit class BInt(val value: Int) extends BData[Int]
implicit class BString(val value: String) extends BData[String]
the 'implicit' declaration makes the compiler "Convert" the types for you, so you can use it like this:
val bDataMap = scala.collection.mutable.Map[String, BData[_]]()
val l = List((1, bDataMap))
l(0)._2 += ("myKey1" -> "Str")
l(0)._2 += ("myKey2" -> 2)
l(0)._2("myKey1").value
and so on....

Type mismatch error in initialization of Scala map (string to object)

I need to setup a Scala Map that maps from String to an object (string, integer, floating point number)
I tried this code:
val m = Map[String, Object]("A"->10, "B"->20.5)
to get type mismatch error.
What could be the solution to this issue?
Strictly speaking Scala Int is not a subtype of Object, but it is a subtype of AnyVal:
val m = Map[String, AnyVal]("A"->10, "B"->20.5)
Where AnyVal is a common super type of all Scala primitives, I always refer to this image which illustrates the type hierarchy:
(source: verrech.net)
If you want a common supertype with scala.Scala.Object or of java.lang.Object use Any.
Here is the link to the image.