In http://lampsvn.epfl.ch/trac/scala/ticket/1342, mustaghattack submitted the following test cases:

File scala/ticket1342/JavaVarArgsInterface.java:

package scala.ticket1342;
interface JavaVarArgsInterface {
   void varArgsMethod( String ... args );
}

File scala/ticket1342/ScalaVarArgsImpl.scala:

package scala.ticket1342
class ScalaVarArgsImpl extends JavaVarArgsInterface {
   def varArgsMethod( args : String*) {
        for( arg <- args ) println( arg )
   }
}

I would now like to use ScalaVarArgsImpl.varArgsMethod both ways around

  1. from a Java main class JavaVarArgsMain
  2. from a Scala main object ScalaVarArgsMain

and I would like to achieve this with 2.7.X as well as with 2.8.0....

Let me first create File scala/ticket1342/ScalaVarArgsMain.scala:

package scala.ticket1342
object ScalaVarArgsMain {
   def main( args : Array[String]) {
        val svai = new ScalaVarArgsImpl
        svai.varArgsMethod(args(0), args(1), args(2))
   }
}

Trying it out with 2.7.X as

the interface

$ javac -d bin27X src/scala/ticket1342/JavaVarArgsInterface.java
$ javap -c -classpath bin27X/ scala.ticket1342.JavaVarArgsInterface
Compiled from "JavaVarArgsInterface.java"
interface scala.ticket1342.JavaVarArgsInterface{
public abstract void varArgsMethod(java.lang.String[]);
}

the implementation

$ scalac27X -d bin27X src/scala/ticket1342/ScalaVarArgsImpl.scala
$ javap -classpath bin27X/ scala.ticket1342.ScalaVarArgsImpl
Compiled from "ScalaVarArgsImpl.scala"
public class scala.ticket1342.ScalaVarArgsImpl extends java.lang.Object implements scala.ticket1342.JavaVarArgsInterface,scala.ScalaObject{
    public scala.ticket1342.ScalaVarArgsImpl();
    public void varArgsMethod(scala.Seq);
    public int $tag()       throws java.rmi.RemoteException;
}

and the cli

$ scalac27X -d bin27X src/scala/ticket1342/ScalaVarArgsMain.scala
$ javap -classpath bin27X/ scala.ticket1342.ScalaVarArgsMain
Compiled from "ScalaVarArgsMain.scala"
public final class scala.ticket1342.ScalaVarArgsMain extends java.lang.Object{
    public static final void main(java.lang.String[]);
    public static final int $tag()       throws java.rmi.RemoteException;
}

now executed like

$ scala27X -cp bin27X scala.ticket1342.ScalaVarArgsMain aaa bbb ccc ddd
aaa
bbb
ccc

For invocation from Java, let me create File scala/ticket1342/JavaVarArgsMain27X.java:

package scala.ticket1342;
import java.util.Arrays;
import java.util.ArrayList;
import scala.collection.jcl.Buffer;
import scala.collection.jcl.Conversions;
class JavaVarArgsMain27X {
   static public void main( String[] args ) {
        ScalaVarArgsImpl svai = new ScalaVarArgsImpl();
        ArrayList al = new ArrayList(Arrays.asList(args[0], args[1], args[2]));
        svai.varArgsMethod((Buffer)Conversions.convertList(al));
   }
}

with compilation going as

$ javac -d bin27X/ -cp /usr/share/java/scala27X/scala-library.jar:bin27X src/scala/ticket1342/JavaVarArgsMain27X.java
Note: src/scala/ticket1342/JavaVarArgsMain27X.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

and execution going as

$ java -cp /usr/share/java/scala27X/scala-library.jar:bin27X scala.ticket1342.JavaVarArgsMain27X aaa bbb ccc ddd
aaa
bbb
ccc

Trying it out with 2.8.0 as

the interface

$ javac -d bin280 src/scala/ticket1342/JavaVarArgsInterface.java
$ javap -c -classpath bin280/ scala.ticket1342.JavaVarArgsInterface
Compiled from "JavaVarArgsInterface.java"
interface scala.ticket1342.JavaVarArgsInterface{
public abstract void varArgsMethod(java.lang.String[]);
}

the implementation

$ scalac280 -d bin280 src/scala/ticket1342/ScalaVarArgsImpl.scala
src/scala/ticket1342/ScalaVarArgsImpl.scala:3: error: class ScalaVarArgsImpl needs to be abstract, since method varArgsMethod in trait JavaVarArgsInterface of type (x$1: <repeated...>[java.lang.String])Unit is not defined
class ScalaVarArgsImpl extends JavaVarArgsInterface {
      ^
one error found
Why doesn't 2.8.0 do it ?

Some investigation showed a difference between 2.7.X and 2.8.0 manifesting in

src/compiler/scala/tools/nsc/typechecker/RefChecks.scala

(not sure whether the root cause is here).

   287        // 2. Check that only abstract classes have deferred members
   288        if (clazz.isClass && !clazz.isTrait) {
   289          def abstractClassError(mustBeMixin: Boolean, msg: String) {
   290            unit.error(clazz.pos,
   291              (if (clazz.isAnonymousClass || clazz.isModuleClass) "object creation impossible"
   292               else if (mustBeMixin) clazz.toString() + " needs to be a mixin"
   293               else clazz.toString() + " needs to be abstract") + ", since " + msg);
   294            clazz.setFlag(ABSTRACT)
   295          }
   296          // Find a concrete Java method that overrides `sym' under the erasure model.
   297          // Bridge symbols qualify.
   298          // Used as a fall back if no overriding symbol of a Java abstract method can be found
   299          def javaErasedOverridingSym(sym: Symbol): Symbol =
   300            clazz.tpe.findMember(sym.name, PRIVATE, 0, false)(NoSymbol).filter(other =>
   301              !other.isDeferred &&
   302              (other hasFlag JAVA) && {
   303                val tp1 = erasure.erasure(clazz.thisType.memberType(sym))
   304                val tp2 = erasure.erasure(clazz.thisType.memberType(other))
   305                atPhase(currentRun.erasurePhase.next)(tp1 matches tp2)
   306              })
**   307          for (member <- clazz.tpe.nonPrivateMembers)**
   308            if (member.isDeferred && !(clazz hasFlag ABSTRACT) &&
   309                !isAbstractTypeWithoutFBound(member) &&
   310                !((member hasFlag JAVA) && javaErasedOverridingSym(member) != NoSymbol)) {
   311              abstractClassError(
   312                false, infoString(member) + " is not defined" + analyzer.varNotice(member))
   313            } else if ((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz)) {
   314              val other = member.superSymbol(clazz);
   315              abstractClassError(true,
   316                infoString(member) + " is marked `abstract' and `override'" +
   317                (if (other != NoSymbol)
   318                  " and overrides incomplete superclass member " + infoString(other)
   319                 else ""))
   320            }

As it turned out, in the cycle starting at line 307, a member named varArgsMethod appeared twice in 2.8.0, once with attributes from the implementation and once with attributes from the abstract one, the latter resulting in an abstractClassError. A naive patch to RefChecks.scala looks like:

   307          for (member <- clazz.tpe.nonPrivateMembers)
   308            if (member.isDeferred && !(clazz hasFlag ABSTRACT) &&
   309                !isAbstractTypeWithoutFBound(member) &&
**   310                !(member hasFlag JAVA) &&**
   311                !((member hasFlag JAVA) && javaErasedOverridingSym(member) != NoSymbol)) {
   312              abstractClassError(
   313                false, infoString(member) + " is not defined" + analyzer.varNotice(member))
   314            } else if ((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz)) {
   315              val other = member.superSymbol(clazz);
   316              abstractClassError(true,
   317                infoString(member) + " is marked `abstract' and `override'" +
   318                (if (other != NoSymbol)
   319                  " and overrides incomplete superclass member " + infoString(other)
   320                 else ""))
   321            }

Now the implementation compiles with 2.8.0:

$ scalac280 -d bin280 src/scala/ticket1342/ScalaVarArgsImpl.scala
$ javap -classpath bin280/ scala.ticket1342.ScalaVarArgsImpl
Compiled from "ScalaVarArgsImpl.scala"
public class scala.ticket1342.ScalaVarArgsImpl extends java.lang.Object implements scala.ticket1342.JavaVarArgsInterface,scala.ScalaObject{
    public scala.ticket1342.ScalaVarArgsImpl();
    public void varArgsMethod(scala.collection.Seq);
}

and the cli

$ scalac280 -d bin280 src/scala/ticket1342/ScalaVarArgsMain.scala
$ javap -classpath bin280/ scala.ticket1342.ScalaVarArgsMain
Compiled from "ScalaVarArgsMain.scala"
public final class scala.ticket1342.ScalaVarArgsMain extends java.lang.Object{
    public static final void main(java.lang.String[]);
}

now executed like

$ scala280 -cp bin280 scala.ticket1342.ScalaVarArgsMain aaa bbb ccc ddd
aaa
bbb
ccc

For invocation from Java, let me create File scala/ticket1342/JavaVarArgsMain280.java:

package scala.ticket1342;
import java.util.Arrays;
import java.util.ArrayList;
import scala.collection.mutable.Buffer;
import scala.collection.JavaConversions;
class JavaVarArgsMain280 {
   static public void main( String[] args ) {
        ScalaVarArgsImpl svai = new ScalaVarArgsImpl();
        ArrayList al = new ArrayList(Arrays.asList(args[0], args[1], args[2]));
        svai.varArgsMethod((Buffer)JavaConversions.asBuffer(al));
   }
}

with compilation going as

$ javac -d bin280/ -cp /usr/share/java/scala280/scala-library.jar:bin280 src/scala/ticket1342/JavaVarArgsMain280.java
Note: src/scala/ticket1342/JavaVarArgsMain280.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

and execution going as

$ java -cp /usr/share/java/scala280/scala-library.jar:bin280 scala.ticket1342.JavaVarArgsMain280 aaa bbb ccc ddd
aaa
bbb
ccc
 
code/javavarargs.txt · Last modified: 2010/02/11 09:10
 
Recent changes RSS feed Valid XHTML 1.0 Driven by DokuWiki