Passing Scala objects as Java Object to Java methods - scala

I have a Java method which takes in a java.lang.Object.
Now I'm trying to pass a Scala Int to it, but the compiler complains about type mismatch of Int and Object.
This feels quite strange as I suppose a Scala Int inherits from AnyRef and should be the same as java.lang.Object?

Scala Int is not extending AnyRef, though - it extends AnyVal, which is not cast-compatible with java.lang.Object, and is more like Java int. Scala also have Integer class, which is just an alias for java.lang.Integer - use that one if you need to pass it as an object.

There're two braches of Scala classes inherited from the Scala root class - Any:
AnyVal: All value based types, e.g. Int, Byte, Double... etc. This is the counterpart of Java raw value classes, i.e. int, float... etc.
AnyRef: All reference based types, e.g. String, List, including boxed Java types, such as java.lang.Integer... etc.
(See diagram in https://docs.scala-lang.org/tour/unified-types.html)
The AnyRef is equivalent to java.lang.Object. Therefore, when calling Java methods taking Object, a AnyVal cannot be directly transferred before boxing. This can be done through explicit type declaration:
JavaClass.methodWithObject(10: java.lang.Integer)
More discussion refer to:
Result type of an implicit conversion must be more specific than AnyRef

Related

How are primitives types objects in Scala?

How are primitive types in Scala objects if we do not use the word "new" to instantiate the instances of those primitives? Programming in Scala by Martin Odersky described the reasoning as some enforcing by a "trick" that makes these value classes to be defined abstract and final, which did not quite make sense to me because how are we able to make an instance of these classes if its abstract? If that same primitive literal is to be stored somewhere let's say into a variable will that make the variable an object?
I assume that you use scala 2.13 with implementation of literal types. For this explanation you can think of type and class as synonyms, but in reality they are different concepts.
To put it all together it worth to treat each primitive type as a set of subtypes each of which representing type of one single literal value.
So literal 1 is a value and type at the same time (instance 1 of type 1), and it is subtype of value class Int.
Let's prove that 1 is subtype of Int by using 'implicitly':
implicitly[1 <:< Int] // compiles
The same but using val:
val one:1 = 1
implicitly[one.type <:< Int] // compiles
So one is kind of an instance (object) of type 1 (and instance of type Int at the same time because because Int is supertype of 1). You can use this value the same way as any other objects (pass it to function or assign to other vals etc).
val one:1 = 1
val oneMore: 1 = one
val oneMoreGeneric: Int = one
val oneNew:1 = 1
We can assume that all these vals contain the same instance of one single object because from practical perspective it doesn't actually matter if this is the same object or not.
Technically it's not an object at all, because primitives came form java (JVM) world where primitives are not objects. They are different kind of entities.
Scala language is trying to unify these two concepts into one (everything is classes), so developers don't have to think too much about differences.
But here are still some differences in a backstage. Each value class is a subtype of AnyVal, but the rest of the classes are subtype of AnyRef (regular class).
implicitly[1 <:< AnyVal] //compiles
implicitly[Int <:< AnyVal] // compiles
trait AnyTraint
implicitly[AnyTraint <:< AnyVal] // fails to compail
implicitly[AnyTraint <:< AnyRef] // compiles
And in addition, because of its non-class nature in the JVM, you can't extend value classes as regular class or use new to create an instance (because scala compiler emulates new by itself). That's why from perspective of extending value classes you should think about them as final and from perspective of creating instances manually you should think of them as abstract. But form most of the other perspectives it's like any other regular class.
So scala compiler can kind of extend Int by 1,2,3 .. types and create instances of them for vals, but developers can't do it manually.

class Int is abstract; cannot be instantiated

While going through Programming in Scala, i came across:
While you can define your own value classes (see Section 11.4), there
are nine value classes built into Scala: Byte, Short, Char, Int, Long,
Float, Double, Boolean, and Unit. The first eight of these correspond
to Java's primitive types, and their values are represented at run
time as Java's primitive values. The instances of these classes are
all written as literals in Scala. For example, 42 is an instance of
Int, 'x' is an instance of Char, and false an instance ofBoolean. You
cannot create instances of these classes using new. This is enforced
by the "trick" that value classes are all defined to be both abstract
and final.
Due to which new Int gives the error class Int is abstract; cannot be instantiated
val a: Int = new Int in Scala. Java allows new Integer(23).
Question: What is the trick the author is taking about. Why Scala defines value classes to be abstract and final.
What is the trick the author is taking about ?
The "trick" is that
when a class is abstract, you cannot make instances of it (cannot call new).
when a class is final, you cannot make subclasses
when a class is abstract and you cannot make subclasses, then you also cannot make a concrete subclass that you could instantiate
So as a result, value classes cannot be instantiated by application code.
Why Scala defines value classes to be abstract and final.
The point of value classes is that they are defined by their (immutable) value/contents. The object identity is not relevant.
The Scala compiler also tries to optimize value classes by not creating any objects at all where possible (just using unboxed primitives directly). That only works if we can be sure that you can just box and unbox at will.
In Java new Integer(1) and another new Integer(1) are two different objects, but that is not useful for a pure value class (if you want to use these different instances as lock monitor objects or something else where you need object identity, you are just confusing yourself and others and should not have used Integer).

Scala: Passing Any to a method which takes java.lang.Object

I'm confused on how Scala's Any relates to java.lang.Object. I know that in scala, AnyRef corresponds to object, but it seems to make a difference whether the method (which takes java.lang.Object) is defined in a java class or a scala class):
the java class:
public class JavaClass {
public static void method(Object input) {
}
}
the scala application:
object ScalaObject extends App{
def method(input:java.lang.Object) = {}
val a:Any = null
method(a) // does not work
JavaClass.method(a) // does work
}
So if the method is in a java-Class, then the compiler allows me to pass a variable of type Any, why is that?
The compiler tries to "make up" for the difference between Scala's and Java's type systems. In Scala, Object =:= AnyRef (they're aliases) and AnyRef <: Any. Therefore, a Scala method that takes Object or AnyRef cannot take an Any or an AnyVal. If you wanted a method that worked on everything, well, then you would have written Any, right?
However, Java methods that take Object are normally meant to work on all values, whether they be actual Objects or primitives (int, long, etc.), and they work due to the boxing conversion of primitives into Objects. Primitives and Object do not have a common supertype like they do in Scala. The Java type system is not expressive enough to differentiate "I only want actual objects," from "I will take anything, be they object or primitive."
Therefore, the Scala compiler patches this up by turning Java methods of Object into methods of Any. This feature is simply to ease interop between the languages. It won't apply this transformation to Scala code though, because if you wanted that behavior then you would have actually written Any instead of Object.
The reason for that is that Any can be either AnyRef or AnyVal, while method can only accept objects which are AnyRef. If you modify the a type to be AnyRef, it is going to work:
def method(input: java.lang.Object) = {}
val a: AnyRef = new Object
method(a)
In case of calling the static Java method, the Scala compiler will turn Any into Object, which also includes boxing of AnyVal values.

Scala's hierarchy mapped to Java's

As "Programming in Scala: A comprehensive step-by-step Guide" states, in Scala there are not basic types values, just objects: Integers are Int instances and doubles are Double instances. I assume that these classes map to Java's Integer, Double ... classes and, therefore, are mapped as Object subclasses.
In the book, the following type hierarchy (classes as types) is presented:
Few pages after this graph is presented, you can read:
What somehow troubles me is: If Scala´s Double maps to Java's Double which is an specification of java.lang.Object and AnyRef is an alias for java.lang.Object too, should't AnyVal be a subclass of AnyRef?
EDIT
Few pages after that I read that primitive types are not mapped to Java's primitive types wrapper classes unless their "boxed" versions are required; but I am still confused since it seems to me that not all Scala's objects are java.lang.Object sublcasses instances. That is: There are classes in Scala which could be not translated in the JVM as Object subclasses.
Java does not only have types that extend java.lang.Object (aka scala.AnyRef), but primitive types, e.g. int, double, boolean, ... In Scala you find them under scala.Any. So a scala.Int corresponds to a Java int. Not java.lang.Integer; not until boxing occurs, a mechanism on the JVM to be able to pass primitives to generic methods. Both Java and Scala do auto-boxing, that is construct a reference around a primitive type when a reference is needed.
The difference in Scala is, it doesn't treat scala.Int any different from say String, it doesn't matter whether the type corresponds to a JVM primitive or not. You can call methods on scala.Int as if it was any regular object. In the byte-code you will still have primitive types.
This is why Scala is sometimes called a true or more pure object-oriented language than Java.

Compile error in scala, why: val num =123;println(num.getClass())

I'm new to scala. I tried this code:
val name = "mike"
println(name.getClass())
It's OK and printed java.lang.String
But, when I try:
val num = 123
println(num.getClass())
There is such a compiler error:
type mismatch; found : Int required: ?{val getClass: ?} Note: primitive types are not implicitly
converted to AnyRef. You can safely force boxing by casting x.asInstanceOf[AnyRef].
I remember scala said "Everything is object in scala", why can't I invoke num.getClass()? And how to fix it?
Yep, everything is an object, but not necessary an instance of a java class/something with a getClass() method :)
Java primitive values (and Unit) are AnyVals in scala (instances of so called value classes), and - whenever it's possible - they are compiled to Java primitives at the end. When it's not possible boxing is done (similar to auto boxing in Java). But - as the error reports - boxing did not happen ("implicitly") in your case. Value classes don't have a getClass() method -> compilation error.
Java classes are AnyRefs (an instance of a reference class = a class instance in Java). getClass will work fine on them: AnyRef is practically the same as java.lang.Object -> it also has a getClass() method that you can call.
As the error recommends you can force the boxing, then getClass() will work on it:
num.asInstanceOf[AnyRef].getClass
will print
class java.lang.Integer
If you want to avoid boxing (e.g. you want to differentiate between primitive and boxed values) have a look at HowTo get the class of _ :Any
The getClass method is only available for reference classes (i.e. scala.AnyRef). 123 is member of a value class (i.e. scala.Any) and thus does not have a getClass method.
See http://www.scala-lang.org/node/128 for the Scala object hierarchy. And www.scala-lang.org/docu/files/api/scala/AnyRef.html for AnyRef.
Everything is object doesn't mean every object has a method getClass.
As the compiler says, 123.asInstanceOf[AnyRef].getClass would work.