1

I'm new to Scala. I need help in resolving this issue.
I have built a project which is written in Scala and I'm trying to integrate it with a Java project. I have taken the dependency on the build scala jar in my Java project in eclipse. However when I call a method in the Scala jar I get ClassCastException when running the application:

Below is the exception I get

Exception in thread "main" java.lang.ClassCastException: com.rockymadden.stringmetric.phonetic.MetaphoneAlgorithm cannot be cast to com.rockymadden.stringmetric.StringFilter at com.rockymadden.stringmetric.phonetic.MetaphoneAlgorithm.compute(MetaphoneAlgorithm.scala:10) at org.gobe.search.dictionary.test.PhoneticTest.main(PhoneticTest.java:16)

What can I do to fix this ?

public static void main(String[] args) {
    MetaphoneMetric metric = new MetaphoneMetric();
    DummyImplicit di = new DummyImplicit();
    MetaphoneAlgorithm algo = new MetaphoneAlgorithm();
    char[] rr = {'h', 'e', 'l', 'l', 'o'};
    System.out.println(algo.compute(rr, di));
}

In the above code "MetaphoneMetric" , "DummyImplicit" are part of Scala Jar I generated.
Logic of Metaphone algorithm can be found at http://pastebin.com/d7CXjDtx

Bourne
  • 1,905
  • 13
  • 35
  • 53
  • What can I do to fix this? – Bourne Jul 29 '13 at 08:22
  • 1
    Show us the logic of the `MetaphoneAlgorithm.compute` method (especially line 10). You are performing an incorrect cast there, it would seem. – Andrzej Doyle Jul 29 '13 at 08:27
  • Please find the logic of MetaphoneAlgorithm.compute() at the pastebin link http://pastebin.com/Dgkb7rQz – Bourne Jul 29 '13 at 09:03
  • Working with full-featured scala classes from java is not the same as doing vise versa, by the way. Also in pasted code there is no class `MetaphoneAlgorithm`, it's an `object`. – dmitry Jul 29 '13 at 09:04
  • My Bad. I pasted the wrong code. Here is the link to the correct code http://pastebin.com/d7CXjDtx. What can I do to fix this issue? – Bourne Jul 29 '13 at 09:16

3 Answers3

3

Looking at the code, your MetaphoneAlgorithm is defined as:

class MetaphoneAlgorithm extends StringAlgorithm[...] { this: StringFilter =>
 ...
}

What is important is this: StringFilter =>. Since StringAlgorithm doesn't already implement StringFilter, mixing this self type is necessary to create a MetaphoneAlgorithm.

In scala you would do:

val algo = new MetaphoneAlgorithm with StringFilter

Edit: as @aim answered, that's exactly what MetaphoneAlgorithm.apply() returns. That is probably what you want.

Or you would create a new class:

class MyAlgo extends MetaphoneAlgorithm with StringFilter

// and then
val algo = new MyAlgo

If you wanted to be able to create a MetaphoneAlgorithm with StringFilter on the java side:

Looking at your StringFilter trait, it should be an interface on the java side.

Edit: Sadly, as it is StringFilter will be turned into an abstract class. The way to provide compatibility with java would be to make StringFilter completely abstract:

trait StringFilter extends Filter[String] with StringFilterable {
  override def filter(charArray: Array[Char]): Array[Char]
}

As well as the Filter trait, and then define MetaphoneAlgorithm.apply() as returning:

def apply(): MetaphoneAlgorithm = new MetaphoneAlgorithm with StringFilter
  with DefaultStringFilter

with DefaultStringFilter being you default StringFilter implementation (the one currently in StringFilter, that also implements the method defined in Filter).

With that change, this becomes possible on the java side:

public class MyAlgo extends MetaphoneAlgorithm implements StringFilter {
  /* implement StringFilter here */
}

And then instantiate that one in your main method.

This is a bit messy, but that allows you to define custom StringFilter implementations on the java side.


The error message itself comes from casting an (unwritten) this to StringFilter, so I can understand why the error is surprising.

As a side note, it is interesting that it lets you directly instantiate the MetaphoneAlgorithm class at all considering it cannot possibly work. I am not sure this can even be prevented, but it is yet another interesting scala/java compatibility problem.

gourlaysama
  • 11,240
  • 3
  • 44
  • 51
  • I'd be interested to see if that last suggestiong works. I agree that `StringFilter` will compile down to a standard interface, but I don't know whether the self-typing assumes anything more than simply casting the `this` instance. – Andrzej Doyle Jul 29 '13 at 09:34
  • I hadn't looked at `StringFilter` hard enough. It has an implementation, and (worse), it inherits from a trait that has an implementation (`Filter[A]`). So it is not possible as is, but it can be made possible, see edit. – gourlaysama Jul 29 '13 at 09:53
  • Will there be any performance hit if I use Scala library instead of using Java library? By performance hit I mean in terms of Runtime space and time complexity? – Bourne Jul 29 '13 at 13:10
  • @bourne: if you mean "any difference between using that Scala library from Scala versus from Java", the answer is no. That's the whole point of running on a common platform (the JVM). If you mean "using this Scala library versus another Java library", then I cannot say much more than "it depends". – gourlaysama Jul 29 '13 at 13:15
1

Please check this:

Predef.DummyImplicit di = new Predef.DummyImplicit();
MetaphoneAlgorithm algo = MetaphoneAlgorithm.apply();
char[] rr = {'h', 'e', 'l', 'l', 'o'};
System.out.println(Arrays.toString(algo.compute(rr, di).get()));
aim
  • 1,461
  • 1
  • 13
  • 26
  • This works. Thanks a ton. can you please provide some explanation on why the above code works and what was I doing wrong? – Bourne Jul 29 '13 at 09:36
  • We just creating default implementation `MetaphoneAlgorithm with StringFilter` as described guys above. Please see [this](https://github.com/rockymadden/stringmetric/blob/master/stringmetric-core/source/core/scala/com/rockymadden/stringmetric/phonetic/MetaphoneAlgorithm.scala#L117). – aim Jul 29 '13 at 09:41
0

You have defined MetaphoneAlgorithm with a self-type annotation:

class MetaphoneAlgorithm { this: StringFilter =>

This means that the class cannot be used unless it has a StringFilter trait mixed in (e.g. new MetaphoneAlgorithm() with MyCoolStringFilter).

Since you're not doing this in your example, where you're just constructing a plain MetaphoneAlgorithm, the code isn't valid. Scalac would let this compile, but the Java compiler doesn't understand Scala's annotations that tell it why this won't work - so you get your error at runtime.

And in fact I don't think it would be possible to do this at all in pure Java code, since you can't create an anonymous class that mixes in a trait*. If you're going to use Scala classes in Java it makes sense to think about what interface you want Java users to call, and then make sure you don't do anything too Scala-y with it.

*Well, I suppose you probably could if you were really motivated to, but you'd have to look at exact how Scala compiles mixins at the bytecode level and then try to imitate that. It would be horribly brittle and not really worthwhile.

Andrzej Doyle
  • 102,507
  • 33
  • 189
  • 228