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.
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.
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.
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)
Related
Consider the following scala class:
class Demo {
def mandatoryInt: Int = 42
def optInt: Option[Int] = Some(1)
def optString: Option[String] = Some("demo")
}
When its methods are accessed from Java code, optString has type Option<String>.
However, method optInt has type Option<Object>.
I guess, it's because basic types are mapped to primitive java types. E.g. mandatoryInt has type int in Java. And Option<int> is not a valid java type.
Does anyone have an idea how to declare optional basic type in scala so that it gets normally inferred in java (i.e. Option[Int] to be seen as Option<Integer>)?
(Scala version 2.12.10, although I doubt that it matters much for my question)
I am trying to get a simple java reflection program working in Scala, and seem to be missing something ...
scala> val cl = new URLClassLoader(Array(new File("Hi.jar").toURI.toURL), getClass.getClassLoader)
cl: java.net.URLClassLoader = java.net.URLClassLoader#3c7b137a
scala> val c = cl.loadClass("Hi")
c: Class[_] = class Hi
scala> val m = c.getMethod("run")
m: java.lang.reflect.Method = public void Hi.run()
scala> m.invoke()
<console>:21: error: not enough arguments for method invoke: (x$1: Any, x$2: Object*)Object.
Unspecified value parameters x$1, x$2.
m.invoke()
^
What am I missing, as the prior line has indicated -
public void Hi.run()
What exactly is it expecting for the two arguments?
Scala is telling you exactly what your problem is: invoke needs 1+ parameters!
See the java doc:
invoke(Object obj, Object... args)
Invokes the underlying method represented by this Method object, on the specified object with the specified parameters.
So, you have to provide at least one argument - a reference to the object (or class) you want to call that method on! As Hi.run() seems to be static, you would want to use your c as only argument to your call.
The following arguments would be the actual parameters that your "reflected" method expects. In your case, no further arguments.
Long story short: you better keep the excellent tutorials from Oracle on reflection close to your scala console while experimenting. If you try to learn "reflection" by trial&error; I guarantee you: a lot of frustrating trials with many strange errors. Really: the reflection API is not very forgiving when you don't know what you are doing; even the slightest mistakes can lead to very unexpected results.
There is nothing specific to Scala there. Method.invoke requires the at least one argument being the instance on which it's applied (or null for a static method).
In Scala, you can use structural typing for such simple case.
It seems for a good reason I need to switch to Scala! So, excuse me if my question might seem novice.
It seems that Any in scala is the supper class of all types and might be considered as an Object type equivalent in Java. But type Any is abstract, while Object is not. More concretely, I can not write the following code in scala (since class Any is abstract and cannot be instantiated):
val k:Any = new Any();
while I can write the following code in Java:
Object k=new Object();
What should I use if I need a concrete equivalent type of java Object type?
AnyRef type which is not abstract seems to be the equivalent of the Java Object Class (see this)
so the following works in scala:
val k:AnyRef = new AnyRef();
AnyRef is the equivalent of java's Object. (Scala unifies the type system rather than having the object/primitive distinction that Java has. So an instance of Any might be a descendant of Object/AnyRef, or an instance of a Java primitive type (e.g. int). (Or it could also be a "value class" descending from AnyVal)).
Example:
scala> String.format("%d", 2)
<console>:8: error: overloaded method value format with alternatives:
(java.util.Locale,java.lang.String,<repeated...>[java.lang.Object])java.lang.String <and>
(java.lang.String,<repeated...>[java.lang.Object])java.lang.String
cannot be applied to (java.lang.String, Int)
String.format("%d", 2)
^
Why can Scala not handle the overloaded format method of the String class ?
Using: Scala 2.9.2 on Windows 7 64 Bit.
Use this instead:
"%d".format(2)
The String.format method is a Java thing, so it's expecting input parameters that are subtypes of java.lang.Object. Scala's Int is not a java.lang.Object:
scala> val i: java.lang.Object = 2
<console>:7: error: type mismatch;
found : Int(2)
required: java.lang.Object
Note: an implicit exists from scala.Int => java.lang.Integer, but
methods inherited from Object are rendered ambiguous. This is to avoid
a blanket implicit which would convert any scala.Int to any AnyRef.
You may wish to use a type ascription: `x: java.lang.Integer`.
val i: java.lang.Object = 2
^
To learn more about this, you should read up on Scala's distinction between AnyVal and AnyRef types. Java has a distinction between objects (like Integer) and primitives (like int), for efficiency. Scala has a similar distinction. All types extend from Any, but "value types" (basically corresponding to primitives) extend from AnyVal and everything else extends from AnyRef (basically java.lang.Object). Read more here.
So, to use String.format you'd have to replace it with a Java Integer, which is an Object:
String.format("%d", new java.lang.Integer(2))
But don't do this; just use it the Scala way, as mentioned above.
The first overloaded definition can't match because the first argument is Locale, while you provided a String.
The second alternative can't match because the second argument is a vararg parameter of Object, but you provided Int. Int is not a subtype of Object (called AnyRef in Scala). Java "fixes" it by auto-boxing, but in the Scala type-system there is no automatic conversion from the primitive type Int to java.lang.Integer.
If the Java definition was generic, something like format[T](f: String, args: T*), Scala would allow your call, because unbounded type parameters in Scala range over primitive types as well.
This is probably a very noobish question, but I was playing a bit with Scala/Java interaction, and was wondering how well did Tuples play along.
Now, I know that the (Type1, Type2) syntax is merely syntactic sugar for Tuple2<Type1, Type2>, and so, when calling a Scala method that returns a Tuple2 in a plain Java class, I was expecting to get a return type of Tuple2<Type1, Type2>
For clarity, my Scala code:
def testTuple:(Int,Int) = (0,1)
Java code:
Tuple2<Object,Object> objectObjectTuple2 = Test.testTuple();
It seems the compiler expects this to be of parameterized types <Object,Object>, instead of, in my case, <Integer,Integer> (this is what I was expecting, at least).
Is my thinking deeply flawed and is there a perfectly reasonable explanation for this?
OR
Is there a problem in my Scala code, and there's a way of being more... explicit, in the cases that I know will provide an API for Java code?
OR
Is this simply a limitation?
Int is Scala's integer type, which is a value class, so it gets special treatment. It is different from java.lang.Integer. You can specify java.lang.Integer specifically if that's what you need.
[dlee#dlee-mac scala]$ cat SomeClass.scala
class SomeClass {
def testIntTuple: (Int, Int) = (0, 1)
def testIntegerTuple: (java.lang.Integer, java.lang.Integer) = (0, 1)
}
[dlee#dlee-mac scala]$ javap SomeClass
Compiled from "SomeClass.scala"
public class SomeClass implements scala.ScalaObject {
public scala.Tuple2<java.lang.Object, java.lang.Object> testIntTuple();
public scala.Tuple2<java.lang.Integer, java.lang.Integer> testIntegerTuple();
public SomeClass();
}