Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Result type of an implicit conversion must be more specific than AnyRef

Tags:

scala

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.

like image 285
elm Avatar asked Oct 24 '14 10:10

elm


4 Answers

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

like image 109
lambdista Avatar answered Nov 20 '22 11:11

lambdista


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(",")
like image 26
Gabriele Petronella Avatar answered Nov 20 '22 11:11

Gabriele Petronella


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?

like image 7
Matthew Avatar answered Nov 20 '22 11:11

Matthew


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.

like image 6
Nate Avatar answered Nov 20 '22 12:11

Nate