简体   繁体   中英

Using java formatter in 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?

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.

You don't specify how someInteger is defined, but I assume you are trying to do something like this:

val someObject: Object = "this"
val someInteger = 3

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

The problem is that when you say someInteger = 3 in Scala, then the variable is Scala's Int and not Java's int , and Java's Formatter doesn't know what to do with it.

If you change your declaration to use an integer type that Java can understand, then it works just fine. For example:

import java.lang.Integer

val someInteger = new Integer(3)

As for why the ugly asInstanceOf version works? A quick check on the REPL will show you what's happening:

scala> 3.asInstanceOf[java.lang.Object].getClass
res0: java.lang.Class[_ <: java.lang.Object] = class java.lang.Integer

So when you call .asInstanceOf[java.lang.Object] on an Int , Scala gives you back a java.lang.Integer .

But really, you should just rewrite the code in a more Scala-ish way. Andrew McCallum would be happier if you did.

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 int s 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.

As dhg has said, the scala compiler treats Scala int types as different to Java int types. You could try importing the JavaConversions implicits and see if that helps. I don't quite understand the exact difference (I would have thought it'd treat them the same).

Alternatively you can use the Scala formatting tools:

val someObject: Object = "this"
val someInteger = 3
val out = "%s-%d ".format(someObject, someInteger)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM