Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using java formatter in scala

Tags:

java

scala

I'm converting someone else's java code to scala (for the curious, it's the example here) and I hit a compiler error on the following (simplified somewhat):

var out = new Formatter(new StringBuilder(), Locale.US)
out.format("%s-%d ", someObject, someInteger);

And here's the error message I get:

[error]   (java.util.Locale,java.lang.String,<repeated...>
[java.lang.Object])java.util.Formatter <and>
[error]   (java.lang.String,<repeated...>[java.lang.Object])java.util.Formatter
[error]  cannot be applied to (java.lang.String, java.lang.Object, Int)
...
[error] one error found

This works if I change the second line to:

out.format("%s-%d ", someObject, someInteger.asInstanceOf[Object]);

Can someone explain why this is?

Does this mean that it's ok in java to pass integers where object arguments are expected but not in scala?

like image 242
JasonMond Avatar asked May 25 '26 05:05

JasonMond


2 Answers

The other answers all add something, but I'm not sure they explain what the problem is.

It all comes down to Int being a class in Scala while int is a primitive in Java. So, in Java, when you write that a method expects an Object, and you pass an int, Java will auto-box it in a java.lang.Integer.

Now, Java's java.lang.Object equivalent in Scala is AnyRef, but Int is not a subclass of AnyRef. There's a proper type for that: Any. An Any can contain both things which are subclasses of java.lang.Object as well as the stuff that are primitives in Java. If you say a method expects Any and pass an Int, then it will be auto-boxed, but not for AnyRef.

So, whenever you interface with Java and a method expects you to pass boxed primitives, you'll have to force the boxing yourself. You can create a method expecting Any in Scala, and then cast it to AnyRef and call the Java equivalent, to make things easier if you are going to call that method a lot.

like image 116
Daniel C. Sobral Avatar answered May 27 '26 19:05

Daniel C. Sobral


The class hierarchy isn't the same in java and scala. java.lang.Object is at the root of the hierarchy in java. In scala, the Any type is at the root. So, anything can be passed to a function which takes a parameter of type Any. However, only subtypes of java.lang.Object can be passed to a function which takes a parameter of type java.lang.Object.

To make matters worse, there's two types of integers. There's scala's Int type, and java.lang.Integer. The former is what you usually get when you set something to a number literal in scala. The latter is what you get with new Integer(3) or 3.asInstanceOf[Integer]. It turns out that the scala Int does not inherit from java.lang.Object, but java.lang.Integer does inherit. As a result you can't pass a scala Int as a parameter expecting a java.lang.Object. That's why this doesn't work in scala.

Java's story is a little weird. In the past it used to be that java ints couldn't be passed where an object was expected; you needed to explicitly convert them into java.lang.Integer. However, a somewhat recent change (version 5) does this for you automatically. This is called autoboxing or unboxing, depending on which way the conversion is going. So that's why it works in java.

like image 31
bchurchill Avatar answered May 27 '26 21:05

bchurchill