30

Let

def h(a: AnyRef*) = a.mkString(",")
h: (a: AnyRef*)String

and so

h("1","2")
res: String = 1,2

However, h(1,2)

error: the result type of an implicit conversion must be more specific than AnyRef
              h(1,2)
                ^
error: the result type of an implicit conversion must be more specific than AnyRef
              h(1,2)
                  ^

This is at least in Scala 2.11.1 and 2.11.1. To ask on a workaround.

elm
  • 20,117
  • 14
  • 67
  • 113
  • 5
    The scala repl on scala 2.10.X gives back a much more meaningful response: `error: type mismatch; found : Int(1) required: AnyRef Note: an implicit exists from scala.Int => java.lang.Integer, but methods inherited from Object are rendered ambiguous. This is to avoid a blanket implicit which would convert any scala.Int to any AnyRef. You may wish to use a type ascription: `x: java.lang.Integer`. h(1,2) ` – Ende Neu Oct 24 '14 at 10:41
  • By the way, you can reproduce the problem with `val x: AnyRef = 42` – Gabriele Petronella Oct 24 '14 at 14:14

4 Answers4

36

The reason is that the numeric type of the literals 1 and 2 is Int which extends AnyVal which, in turn, extends Any. On the other hand String extends AnyRef which, in turn, extends Any. So as you can see AnyVal (Int's parent) does not extend AnyRef. You can solve this in one of two ways.

The first one is changing the type from AnyRef to Any as described by Nate.

The second one is using a type ascription for the literals 1 and 2 so that they are considered of type java.lang.Integer which extends java.lang.Object. Note also that AnyRef is just an alias for java.lang.Object. So, using your definition the following should work:

scala> h(1: java.lang.Integer, 2: java.lang.Integer)
res2: String = 1,2

More info on Scala Hierarchy

lambdista
  • 1,850
  • 9
  • 16
25

You can reproduce the issue simply with:

val x: AnyRef = 42

Here's the relevant pull request on github that introduced the change

The rationale is that for security reasons some implicit conversions are explicitly disabled, namely when the conversion goes from T to U is disabled if:

T <: Null

or

AnyRef <: U

In your specific case, this means that an Int (which is not an AnyRef) will never be converted to AnyRef.

If you need to accept both Int and String, you can consider accepting Any instead. Since every scala object inherits from Any, there's no implicit conversion needed.

def h(a: Any*) = a.mkString(",")
Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
7

Cast your variable to AnyRef by doing something like this:

h(1.asInstanceOf[AnyRef], 2.asInstanceOf[AnyRef])

Why?

In scala not everything extends Object (aka AnyRef) in the way that it would in java. Specifically primitives extend AnyVal, so if your function requires an AnyRef you'll need to cast / convert / restrict your scala variables.

There's a good discussion here: What are the relationships between Any, AnyVal, AnyRef, Object and how do they map when used in Java code?

Matthew
  • 10,361
  • 5
  • 42
  • 54
6

I don't think you want to use AnyRef here. I think you want Any.

scala> def h(a: Any*) = a.mkString(",")
h: (a: Any*)String

scala> h(1,2)
res0: String = 1,2

The reason is that the numeric value 5 is an Int, but AnyRef is java's Object equivalence. So to invoke that method it would need to be a java.util.Integer.

Nate
  • 2,205
  • 17
  • 21