2

I've been programming in Scala since 2010 (it's the end of 2017 now) and was shocked today to find out that the java.util.Arrays.toString method is not overloaded for Strings. The following works as expected in Java, but not in Scala.

Java

public class Main {
  public static void main(String[] args) {
    System.out.println(java.util.Arrays.toString(args));
  }
}

Scala

object Main {
  def main(args: Array[String]): Unit = {
    println(java.util.Arrays.toString(args))
  }
}

I know many workarounds for this (so no need to suggest any), but I would assume I wouldn't need one in the first place. I can neither imagine this being a bug (or rather an unfinished feature) for so many years nor can I imagine this behavior being intended.

<console>:12: error: overloaded method value toString with alternatives:
  (x$1: Array[Object])String <and>
  (x$1: Array[Double])String <and>
  (x$1: Array[Float])String <and>
  (x$1: Array[Boolean])String <and>
  (x$1: Array[Byte])String <and>
  (x$1: Array[Char])String <and>
  (x$1: Array[Short])String <and>
  (x$1: Array[Int])String <and>
  (x$1: Array[Long])String
 cannot be applied to (Array[String])

Can someone come up with a reasonable explanation why this is not working? Please don't be cheeky, I see that it's not overloaded for Strings, but there is clearly some hack in Java to make it work, why is there none in Scala?

I found this question, but again it works in Java, why doesn't it in Scala?

PS

Not that it matters, but I got:

  • Scala v2.12.4
  • Java v1.8.0_151
agilesteel
  • 16,775
  • 6
  • 44
  • 55

1 Answers1

1

It is actually a bug in java that it works. If you look at the list of candidates, you'll see, that clearly there is no suitable alternative. It ends up calling Array[Object] variant by accident, but that is wrong, becuase Array is invariant in its type parameter (all generic types are invariant in java), so Array[Object] is not a superclass of Array[String].

Dima
  • 39,570
  • 6
  • 44
  • 70
  • 2
    This is not true. [Arrays are covariant](https://stackoverflow.com/a/18666878/1349366) in Java. – Aivean Nov 22 '17 at 22:42
  • @Aivean ok, I'll rephrase: it is a bug, that arrays are covariant in java. :) They really shouldn't be. Consider this: `void foo(Object[] a) { a[0] = "foo"; }` and then `foo(new Integer[] {0});` This compiles without warning (because of the same bug), and then crashes at run time. – Dima Nov 22 '17 at 23:18
  • I think a better way to look at it is: arrays are really invariant, but the compiler lets you treat them as covariant, which seems like a bug (or rather a (semi-intentional) flaw in the implementation). That's what I tried to convey in my answer. – Dima Nov 22 '17 at 23:21
  • 2
    ok, so, to conclude, for legacy reasons in Java arrays are covariant, and `Arrays.toString` relies on that. In scala `Array` variance is fixed (it's invariant), so java's `toString(Array[AnyRef])` can no longer be used with `Array[String]`. – Aivean Nov 22 '17 at 23:56
  • This makes sense. Thx for the explanation. – agilesteel Nov 23 '17 at 21:37