Prakash A
Prakash A

Reputation: 27

Scala Generic method errors on parameter operation

I get "type mismatch; found : Int(1) required: String" error when I try to return the incremented value of the input parameter from a Scala generic method below.

I did try using the Case method for this but it did not work as well. Basically I want to decide the operation based on input Type to the method and return the calculated/modified value.

object GenericOperations {

//  def increment[typ](val x:typ):typ = x match {
//    case _:Int => x + 1
//    case _:String => x + "x"
//  }

    def increment2(x:Any):Any = {
        if(x.isInstanceOf[Int]) {
          x+1
        }
        else if (x.isInstanceOf[String]) {
          x + "x"
        }
        else {
          println("No Match type")
        }
    }
}

Upvotes: 0

Views: 346

Answers (3)

den_po
den_po

Reputation: 96

I would rather use method overloading:

  def increment2(x: Int) = x + 1
  def increment2(x: String) = x + "x"

If you are sure you need exactly one function you may use match.

  def increment2(x: Any): Any = x match {
    case v: Int => v + 1
    case v: String => v + "x"
    case _ =>
      throw new Exception("No Match type")
  }

But returning Any isn't good thing as you cannot use its result without type cast

GenericOperations.increment2(3) + 3 // type mismatch

Still you may use the same match way:

  def increment2[T](x: T): T = (x match {
    case v: Int => v + 1
    case v: String => v + "x"
    case _ => throw new Exception("No Match type")
  }) match {
    case v: T => v
    case _ => throw new Exception("Invalid increment expression result type")
  }

As it has been mentioned in the comments there is also typeclass way:

//type class
trait Incrementable[T] {
  def inc(x: T): T
}

//type class instance for String
implicit val incString = new Incrementable[String] {
  def inc(x: String) = x + "x"
}

//type class instance for Int, single abstract method (SAM) form
implicit val incInt: Incrementable[Int] = (x: Int) => x + 1

def increment2[T: Incrementable](x: T): T = implicitly[Incrementable[T]].inc(x)

Upvotes: 3

Jörg W Mittag
Jörg W Mittag

Reputation: 369458

You have declared x to be of type Any. Which means you are only allowed to use the methods of Any.

You are calling x.+(1). There is no + method in Any. Therefore, you can't use +.

You should be getting an error about + not existing, but you don't, so what's happening here?

There is an implicit conversion for string concatenation, which can convert an arbitrary object into a String and then concatenate another String to it. In this case, it converts x to an any2stringadd and then tries to add 1 to it, but the any2stringadd.+ method only takes a String as its argument, and thus you get the strange error message that it is expecting a String.

You, however, are passing 1 as an argument, which is an Int not a String.

Note that any2stringadd is deprecated in Scala 2.13, so in the future you would just get an error about a non-existent method.

Note that you have tagged this question with and also talk about generics multiple times in the subject and the question body, yet there are no generics in your code. In fact, with generics, this problem would not exist.

See also

Upvotes: 2

amer
amer

Reputation: 1672

Maybe something like this , even though I still don't like it because the usage of getOrElse , but here you go anyway:

    object GenericOperations {

    //  def increment[typ](val x:typ):typ = x match {
    //    case _:Int => x + 1
    //    case _:String => x + "x"
    //  }

        def increment2(x:Any):Any = {
            if(x.isInstanceOf[Int]) {
              toInt(x).getOrElse(0)+1
            }
            else if (x.isInstanceOf[String]) {
              x + "x"
            }
            else {
              println("No Match type")
            }
        }

        def toInt(x: Any): Option[Int] = x match {
          case i: Int => Some(i)
          case _ => None
        }
}

Upvotes: 1

Related Questions