1

I have a method that should dynamically cast a field member into a certain type depending on a configuration flag format.

This flag format, accepts one of the following type values:

object Types {
    val Str = "string"
    val IntNum1 = "int"
    val IntNum2 = "integer"
    val DoubleNum = "double"
    val LongNum = "long"
}

One option would be to use reflection. Another option would be to use pattern matching (the method I'm trying to do this with)

Here's the method for String types:

private def getValClassIfStr(format: String): Class[String] = {
    format.toLowerCase match {
        case Types.Str => classOf[String]
        case _ => null
    }
}

And Here's the method for Numerical types, extending from AnyVal, that fails to compile:

private def getValueClass[T <: AnyVal](format: String): Try[Class[T]] = {
    format.toLowerCase match {
        case Types.IntNum1 || Types.IntNum2 => Success(classOf[Int])
        case Types.DoubleNum => Success(classOf[Double])
        case Types.LongNum => Success(classOf[Long])
        case _ => Failure(new Exception)
    }
}

The second method doesn't compile because of a: Type mismatch: required Try[Class[T]] found Try[Class[Int]]

Which I don't understand, given T <: AnyVal.

Any idea? and if that's a bad approach, what would a "clean" alternative be?

Mehdi
  • 765
  • 9
  • 19
  • 1
    https://stackoverflow.com/questions/56822188/using-same-type-parameter-as-argument-type-and-parameter-type-with-match-express https://stackoverflow.com/questions/21285235/pattern-matching-on-generic-type-in-scala – Dmytro Mitin Jan 05 '20 at 17:20
  • I would just create my own **ADT** of types and my own parser. – Luis Miguel Mejía Suárez Jan 05 '20 at 17:23
  • 3
    @Mehdi B. : What will happen if someone calls `getValueClass[Double]("int")` ? `Type mismatch` error is because of this. Compiler does not believe that actual return type will conform to declared return type. – asanand Jan 05 '20 at 18:49
  • @asanand Thanks, that makes sense. – Mehdi Jan 06 '20 at 09:51

1 Answers1

1

I think the reason why this doesn't work becomes clearer once you replace Class[T] in the return value with Class[AnyVal]:

type mismatch;
 found   : Class[Int](classOf[scala.Int])
 required: Class[AnyVal]
Note: Int <: AnyVal, but Java-defined class Class is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: AnyVal`. (SLS 3.2.10)

In other words, any call to getValueClass[T] would always have to be populated with T = AnyVal by the compiler, since format is only evaluated at runtime. This, however, is not possible since Class[T] is invariant, so you can't return a Class[Int] and a Class[Double] as a Class[AnyVal], since for invariant containers there's no subtyping relationship.

To demonstrate:

var x: Class[AnyVal] = _
x = classOf[Int]
// Boom
fresskoma
  • 25,481
  • 10
  • 85
  • 128