views:

312

answers:

5

I'm trying to inject some Scala code into my existing Java app. (So, being said, I want some more fun).

I create a singleton stuff in Scala

ScalaPower.scala

    package org.fun
    class ScalaPower
    object ScalaPower{
      def showMyPower(time:Int) = {
        (0 to time-1).mkString(", ")
      }
    }

Now, inside OldJava.java

class OldJava {
  public void demo(){
    System.out.println(?)
  }
}

What should I fill in ? so that Java will call the showMyPower method? I tried both org.fun.ScalaPower.showMyPower(10) and org.fun.ScalaPower.getInstance().showMyPower(10) but none work.

(Decompile the class file using Jad show me nothing but nonsense code.)

Edit I remove the class ScalaPower declaration and scala produce the static method as expected. (call to org.fun.ScalaPower.showMyPower(10) just works).

Wonder if it's a bug in scala compiler or not

+3  A: 

Keep in mind that the stock javap tool can be used to see what the Scala compiler produces. It doesn't directly answer your question, of course, but when what you need is just to be reminded of the code generation patterns, it's sufficient.

Randall Schulz
Thanks. I'll try it when I'm back home. (At work now)
Phương Nguyễn
+7  A: 

What were the errors you were getting? Using your Scala sample and the following Java class:

cat test.java:


import org.fun.*;

public class test {
    public static void main(String args[]) {
       System.out.println("show my power: " + ScalaPower.showMyPower(3));       
    }
}

And running it as follows:

java -cp .:<path-to/scala/install-dir>/lib/scala-library.jar test

gives my the output:

show my power: 0, 1, 2

Arjan Blokzijl
Hmm, it's weird. My code doesn't even compile. It is saying `"Symbol 'showMyPower' not found"` (although IDEA Scala Plugin understand it, and offer me an auto-completion)
Phương Nguyễn
I findout that actually the method `showMyPower` is declared inside Scala$, not Scala. Which version of Scala you are using that make these code compilable? I'm using 2.7.5
Phương Nguyễn
Ouch, `org.fun.ScalaPower$.MODULE$.showMyPower` is indeed the correct method I should call, What the heck?
Phương Nguyễn
Damn it, I remove `class ScalaPower` declaration before the `object ScalaPower` and it compiled like you was talking about.
Phương Nguyễn
I'm using Scala 2.8.0 final, could you perhaps also try with that version? There are two classes generetad, ScalaPower and ScalaPower$ (the latter with the MODULE$ field as you described). ScalaPower has a method public static final java.lang.String showMyPower(int); which I could call as described above.
Arjan Blokzijl
+2  A: 

I think this indirectly covers it:

Companion Objects and Java Static Methods

There is one more thing to know about companion objects. Whenever you define a main method to use as the entry point for an application, Scala requires you to put it in an object. However, at the time of this writing, main methods cannot be defined in a companion object. Because of implementation details in the generated code, the JVM won’t find the main method. This issue may be resolved in a future release. For now, you must define any main method in a singleton object (i.e., a “non-companion” object) [ScalaTips]. Consider the following example of a simple Person class and companion object that attempts to define main.

As found here: http://programming-scala.labs.oreilly.com/ch06.html

In short because your Object is a companion object (has a companion class) you can't call it like you expect. As you found if you get rid of the class it will work.

Ry4an
This is no longer true. Somewhere between when that was written and when Scala 2.8 was released, the forwarding code generation patterns were fixed to allow main methods in companions.
Randall Schulz
Oooh, excellent. The book hinted they wished it would be changed, but didn't say for 2.8 (and they had a lot of looking forward to 2.8-ness).That said the original questioner doesn't give his version, and from his "Edit I remove the class ScalaPower declaration and scala produce the static method as expected." it still sounds like that might have been the problem.
Ry4an
Thanks. That explain my situation. I'm switching to scala 2.8 right now. Hell, the eclipse scala plugin still sucks.
Phương Nguyễn
A: 

After doing

class ScalaPower 
object ScalaPower{
  def showMyPower(time:Int) = {
    (0 to time-1).mkString(", ")
  }
}

ScalaPower.showMyPower(10) works as expected.

Jus12
+2  A: 

It's usually better to access the singleton directly from its own class.

In this case:

org.fun.ScalaPower$.MOUDLE$.showMyPower(10);

Without going too much into the implementation details, Scala differentiates namespaces between Object and Class/Trait. This means they can use the same name. However, an object has a class, and therefore needs a mangled name on the JVM. The current Scala conventions is to add a $ at the end of the module name (for top-level modules). If the object is defined in a class, I believe the convention is OuterClass$ModuleName$. To enforce the singleton property of the ScalaPower module, there is also a static MODULE$ member of the ModuleName$ class. This is initialised at class-load time, ensuring that there is only one instance. A side effect of this is that you should not do any sort of locking in a module's constructor.

In any case, Scala also has built into it a "make things nicer for Java" static-forwarders mechanism. This is where it writes static methods on the ScalaPower class that just call ScalaPower$.MODULE$.someMethod(). If you also define a companion class, the forwarders that could be generated are limited, as you are not allowed to have naming conflicts with static and instance-level methods on the JVM. I think in 2.8.0 this means if you have a companion object, you lose your static forwarders.

In this case a "best practice" would be to always use the ScalaPower$.MODULE$ reference instead of a static forwarder, as the forwarder could disappear with modifications to the ScalaPower class.

jsuereth