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.