Understanding the Type System and Java Interop

65 views
Skip to first unread message

Bastian, Mark

unread,
Mar 22, 2012, 10:26:10 AM3/22/12
to scala...@googlegroups.com
Hi Folks,

I have an interesting problem that I am not understanding. Hopefully you
can tell me exactly what is going on here.

I have a trait that attempts to return instances of its implementing class
defined like so:

trait IStringPair[T] {
def a : String
def b : String

def build(a : String, b : String) : T
def cat(that : IStringPair[T]) = build(this.a + that.a, this.b + that.b)
override def toString = a + b
}

Here is a simple implementation:

class StringPair(val a : String, val b : String) extends
IStringPair[StringPair] {
def build(a : String, b : String) = new StringPair(a, b)
def len = a.length + b.length
}

I can paste this into a repl session and do stuff like this:

val a = new StringPair("A", "B")
val b = new StringPair("1", "2")
val c = a cat b
println(c.len)


So far, so good.

Now, I try to interop this in Java. I put the above into a package named
"types" and create the class shown at the end of this message.

Here is the issue. When I call pass, everything works as expected. When I
call fail, I get a no such method error. I have no idea what I am doing
wrong here. If I run the command :javap -c -private types.StringPair from
the repl, I get public java.lang.Object cat(types.IStringPair). This seems
like it ought to work as StringPair is an IStringPair, as shown in the
dump (public class types.StringPair extends java.lang.Object implements
types.IStringPair,scala.ScalaObject).

Anyone willing to enlighten me on this? I would certainly appreciate it.
Thanks!

//Example demonstrating the issue
package types;

public class JMain {

public static void main(String[] args) {
pass();
fail();
}

public static void pass() {
System.out.println("This works.....");
IStringPair a = new StringPair("A", "B");
IStringPair b = new StringPair("1", "2");
StringPair c = (StringPair) a.cat(b);
System.out.println(c.len());
}

public static void fail() {
System.out.println("This produces:\n[error] java.lang.NoSuchMethodError:
types.StringPair.cat(Ltypes/IStringPair;)Ltypes/StringPair;");
StringPair a = new StringPair("A", "B");
StringPair b = new StringPair("1", "2");
StringPair c = (StringPair) a.cat(b);
System.out.println(c.len());
System.out.println(c.len());
}
}


Dave

unread,
Mar 22, 2012, 12:31:00 PM3/22/12
to scala-user
I think it is type erasure

C:\scala-2.10.0-M2\examples\testjava>javac -cp ".;C:\scala-2.10.0-
M2\lib\*" JMain.java -d . -Xlint:unchecked
JMain.java:17: warning: [unchecked] unchecked call to
cat(types.IStringPair<T>)
as a member of the raw type types.IStringPair
StringPair c = (StringPair) a.cat(b);
^
1 warning

Runtime casting is already spooky. Runtime casting with unchecked type
erased objects is even spookier.

C:\scala-2.10.0-M2\examples\testjava>java -cp ".;C:\scala-2.10.0-M2\lib
\*" types
.JMain
This works.....
4
This produces:
[error] java.lang.NoSuchMethodError: types.StringPair.cat(Ltypes/
IStringPair;)Lt
ypes/StringPair;
Exception in thread "main" java.lang.NoSuchMethodError:
types.StringPair.cat(Lty
pes/IStringPair;)Ltypes/StringPair;
at types.JMain.fail(JMain.java:26)
at types.JMain.main(JMain.java:9)
> }- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -

Bastian, Mark

unread,
Mar 22, 2012, 12:48:12 PM3/22/12
to Dave, scala-user
I figured it probably has to do with either type erasure or something I am
not understanding in the Scala type system, but it still seems like it
ought to work.

If the generated bytecode is:

public java.lang.Object cat(types.IStringPair);
Code:
0: aload_0
1: aload_1
2: invokestatic #14; //Method
types/IStringPair$class.cat:(Ltypes/IStringPair;Ltypes/IStringPair;)Ljava/l
ang/Object;
5: areturn


And StringPair is clearly (I think) an instance of an IStringPair (see
below), shouldn't it work? Type erasure turns the return type into a
java.lang.Object, which is why I do the cast on the return value in the
Java code.


//From the javap dump


public class types.StringPair extends java.lang.Object implements
types.IStringPair,scala.ScalaObject

Any tips on how to get this to work or if it is even possible?

Thanks!

Dave

unread,
Mar 22, 2012, 1:25:55 PM3/22/12
to scala-user
You could use a type member instead of a type parameter:

package types
trait IStringPair {
type T
def a : String
def b : String
def build(a : String, b : String) : T
def cat(that : IStringPair) = build(this.a + that.a, this.b +
that.b)
override def toString = a + b
}

class StringPair(val a : String, val b : String) extends IStringPair {
type T = StringPair
def build(a : String, b : String) = new StringPair(a, b)
def len = a.length + b.length
}





On 22 mrt, 17:48, "Bastian, Mark" <mbas...@sandia.gov> wrote:
> I figured it probably has to do with either type erasure or something I am
> not understanding in the Scala type system, but it still seems like it
> ought to work.
>
> If the generated bytecode is:
>
> public java.lang.Object cat(types.IStringPair);
>   Code:
>    0:   aload_0
>    1:   aload_1
>    2:   invokestatic    #14; //Method
> types/IStringPair$class.cat:(Ltypes/IStringPair;Ltypes/IStringPair;)Ljava/l
> ang/Object;
>    5:   areturn
>
> And StringPair is clearly (I think) an instance of an IStringPair (see
> below), shouldn't it work? Type erasure turns the return type into a
> java.lang.Object, which is why I do the cast on the return value in the
> Java code.
>
> //From the javap dump
> public class types.StringPair extends java.lang.Object implements
> types.IStringPair,scala.ScalaObject
>
> Any tips on how to get this to work or if it is even possible?
>
> Thanks!
>
> >> - Tekst uit oorspronkelijk bericht weergeven -- Tekst uit oorspronkelijk bericht niet weergeven -

Johannes Rudolph

unread,
Mar 22, 2012, 1:36:22 PM3/22/12
to Dave, scala-user
On Thu, Mar 22, 2012 at 5:31 PM, Dave <dave.mah...@hotmail.com> wrote:
> I think it is type erasure

I don't think so. Type erasure would normally produce a
ClassCastException. A NoSuchMethodError almost always points to a
problem with the static signatures of method calls. I haven't looked
at it closely but it may be related to
https://issues.scala-lang.org/browse/SI-3452

I think it makes sense to look in the issue tracker for other bugs
mentioning NoSuchMethodErrors and if you don't find anything file a
new report.

Johannes

--
Johannes

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

Dave

unread,
Mar 22, 2012, 2:28:38 PM3/22/12
to scala-user
Yes I think that is the one

INVISIBLE O NOOS
================
C:\scala-2.10.0-M2\examples\testjava>scalac -Ycheck:genjvm types.scala
types.scala:10: warning: compiler bug: created generic signature for
method cat
in types.StringPair that does not conform to its erasure
signature: (Ltypes/IStringPair<Ltypes/StringPair;>;)Ltypes/StringPair;
original type: (that: types.IStringPair)types.StringPair
normalized type: (that: types.IStringPair)types.StringPair
erasure type: (that: types.IStringPair)Object
if this is reproducible, please report bug at http://lampsvn.epfl.ch/trac/scala
class StringPair(val a : String, val b : String) extends
IStringPair[StringPair]
{
^
one warning found



On 22 mrt, 18:36, Johannes Rudolph <johannes.rudo...@googlemail.com>
wrote:
> On Thu, Mar 22, 2012 at 5:31 PM, Dave <dave.mahabiers...@hotmail.com> wrote:
> > I think it is type erasure
>
> I don't think so. Type erasure would normally produce a
> ClassCastException. A NoSuchMethodError almost always points to a
> problem with the static signatures of method calls. I haven't looked
> at it closely but it may be related tohttps://issues.scala-lang.org/browse/SI-3452
> Johannes Rudolphhttp://virtual-void.net- Tekst uit oorspronkelijk bericht niet weergeven -

Dave

unread,
Mar 22, 2012, 2:31:06 PM3/22/12
to scala-user
So it is signature erasure.

Som Snytt

unread,
Mar 22, 2012, 3:09:00 PM3/22/12
to scala-user
That scalac option -Ycheck:genjvm is good to know. It doesn't just
warn, but attempts to fix the signature?

One workaround, which gives the interesting trait method a result type
erased to IStringPair; then use asT to downcast:

trait IStringPair2[T <: IStringPair2[T]] {
def build(a : String, b : String) : IStringPair2[T]
def asT: T = this.asInstanceOf[T]
}
class StringPair2(val a : String, val b : String) extends
IStringPair2[StringPair2] {
}

Java seems OK then with
StringPair2 c = a.cat(b).asT();

Dave

unread,
Mar 22, 2012, 3:19:13 PM3/22/12
to scala-user


On 22 mrt, 20:09, Som Snytt <som.sn...@gmail.com> wrote:
> That scalac option -Ycheck:genjvm is good to know.  It doesn't just
> warn, but attempts to fix the signature?
>

No I don't think it tries to fix it. It informs about the bug and
urges to report it.


Som Snytt

unread,
Mar 22, 2012, 3:48:40 PM3/22/12
to scala-user
I've still got flu head, so, grain of salt, but the test succeeds with
the flag and fails without. Which could be confusing. It looks like
the compiler bails out of addGenericSignature after emitting the
warning.

apm@halyard ~/tmp
$ rm nomethod/*.class

apm@halyard ~/tmp
$ scalac -Ycheck:genjvm nomethod.scala
nomethod.scala:36: warning: compiler bug: created generic signature
for method cat in nomethod.StringPair that does not conform to its
erasure
signature: (Lnomethod/IStringPair<Lnomethod/StringPair;>;)Lnomethod/StringPair;
original type: (that: nomethod.IStringPair)nomethod.StringPair
normalized type: (that: nomethod.IStringPair)nomethod.StringPair
erasure type: (that: nomethod.IStringPair)java.lang.Object


if this is reproducible, please report bug at http://lampsvn.epfl.ch/trac/scala

class StringPair(val a : String, val b : String) extends


IStringPair[StringPair] {
^
one warning found

apm@halyard ~/tmp
$ javac -Xlint:unchecked -classpath
".;O:/scala-2.9.1.final/lib/scala-library.jar" nomethod/JMain.java

apm@halyard ~/tmp
$ java -classpath ".;O:/scala-2.9.1.final/lib/scala-library.jar" nomethod.JMain
This works.....
A1B2
4
(2) This produces:
[error] java.lang.NoSuchMethodError:
types.StringPair.cat(Ltypes/IStringPair;)Ltypes/StringPair;
A1B2


4
This produces:
[error] java.lang.NoSuchMethodError:
types.StringPair.cat(Ltypes/IStringPair;)Ltypes/StringPair;

A1B2
4

apm@halyard ~/tmp
$ rm nomethod/*.class

apm@halyard ~/tmp
$ scalac nomethod.scala

apm@halyard ~/tmp
$ javac -Xlint:unchecked -classpath
".;O:/scala-2.9.1.final/lib/scala-library.jar" nomethod/JMain.java

apm@halyard ~/tmp
$ java -classpath ".;O:/scala-2.9.1.final/lib/scala-library.jar" nomethod.JMain
This works.....
A1B2
4
(2) This produces:
[error] java.lang.NoSuchMethodError:
types.StringPair.cat(Ltypes/IStringPair;)Ltypes/StringPair;
A1B2


4
This produces:
[error] java.lang.NoSuchMethodError:
types.StringPair.cat(Ltypes/IStringPair;)Ltypes/StringPair;
Exception in thread "main" java.lang.NoSuchMethodError:

nomethod.StringPair.cat(Lnomethod/IStringPair;)Lnomethod/StringPair;
at nomethod.JMain.fail(JMain.java:37)
at nomethod.JMain.main(JMain.java:9)

Dave

unread,
Mar 22, 2012, 4:25:44 PM3/22/12
to scala-user
Ah yeah, indeed. Now I see.
Looks like it fixed the problem in runtime. But still it is an
unchecked call for which the java compiler warns.

C:\scala-2.10.0-M2\examples\testjava>scalac -Ycheck:genjvm types.scala
types.scala:10: warning: compiler bug: created generic signature for
method cat
in types.StringPair that does not conform to its erasure
signature: (Ltypes/IStringPair<Ltypes/StringPair;>;)Ltypes/StringPair;
original type: (that: types.IStringPair)types.StringPair
normalized type: (that: types.IStringPair)types.StringPair
erasure type: (that: types.IStringPair)Object
if this is reproducible, please report bug at http://lampsvn.epfl.ch/trac/scala
class StringPair(val a : String, val b : String) extends
IStringPair[StringPair]
{
^
one warning found

C:\scala-2.10.0-M2\examples\testjava>javac -cp ".;C:\scala-2.10.0-
M2\lib\*" JMai
n.java -d . -Xlint:unchecked
JMain.java:17: warning: [unchecked] unchecked call to
cat(types.IStringPair<T>)
as a member of the raw type types.IStringPair
StringPair c = (StringPair) a.cat(b);
^
1 warning

C:\scala-2.10.0-M2\examples\testjava>java -cp ".;C:\scala-2.10.0-M2\lib
\*" types
.JMain
This works.....
4
This produces:
[error] java.lang.NoSuchMethodError: types.StringPair.cat(Ltypes/
IStringPair;)Lt
ypes/StringPair;
4


On 22 mrt, 20:48, Som Snytt <som.sn...@gmail.com> wrote:
> I've still got flu head, so, grain of salt, but the test succeeds with
> the flag and fails without.  Which could be confusing.  It looks like
> the compiler bails out of addGenericSignature after emitting the
> warning.
>
> apm@halyard ~/tmp
> $ rm nomethod/*.class
>
> apm@halyard ~/tmp
> $ scalac -Ycheck:genjvm nomethod.scala
> nomethod.scala:36: warning: compiler bug: created generic signature
> for method cat in nomethod.StringPair that does not conform to its
> erasure
> signature: (Lnomethod/IStringPair<Lnomethod/StringPair;>;)Lnomethod/StringPair;
> original type: (that: nomethod.IStringPair)nomethod.StringPair
> normalized type: (that: nomethod.IStringPair)nomethod.StringPair
> erasure type: (that: nomethod.IStringPair)java.lang.Object
> if this is reproducible, please report bug athttp://lampsvn.epfl.ch/trac/scala
> > urges to report it.- Tekst uit oorspronkelijk bericht niet weergeven -

Som Snytt

unread,
Mar 22, 2012, 5:46:24 PM3/22/12
to scala-user
On the Java side, use generics as usual, instead of the raw type, to
avoid the warning.

IStringPair<StringPair> a = new StringPair("A", "B");
IStringPair<StringPair> b = new StringPair("1", "2");
StringPair c = a.cat(b);

Also, in the original failing case, no cast is needed to compile
(suspiciously; that's without -check:genjvm)

StringPair a = new StringPair("A", "B");
StringPair b = new StringPair("1", "2");

StringPair c = a.cat(b);

Reply all
Reply to author
Forward
0 new messages