Search code examples
scalatypesany

Type parameter vs Any in Scala


I have a class RBase, and a RInt class that inherits from it. The Base has three interface functions. As the subclasses that inherit can use different type of values, the function parameter is typed as Any. As a result, I need to use asInstanceOf to implement the subclasses. This is an example.

abstract class RBase(val name:String)
{
    def encode(value:Any) : Array[Byte]
    def decode(byteArray: Array[Byte]) : Any

    def check(value:Any) : Boolean
}

class RInt extends RBase("int")
{
    override def encode(value: Any) = {
        val byteValue = value.asInstanceOf[Int]
        Array[Byte](byteValue.toByte, byteValue.toByte, byteValue.toByte)
    }
    override def decode(byteArray: Array[Byte]) : Any = {
         byteArray.size   
    }
    override def check(value:Any) : Boolean = {
        val byteValue = value.asInstanceOf[Int]
        if (byteValue.toInt > 0) true
        else false
    }
}


object Main extends App {
    val b = new RInt
    println(b.decode(Array[Byte]()))
    println(b.encode(100).mkString(":"))
    println(b.check(-1))
}

// uncomment when compile
Main.main(args)

I think Any and asInstanceOf can be removed using type parameters. This is my first try.

abstract class TBase(val name:String)
{
    def encode[T](value:T) : Array[Byte]
    def decode[T](byteArray: Array[Byte]) : T
    def check[T](value:T) : Boolean
}

class TInt extends TBase("bit")
{
    override def encode[Int](value: Int) = {
        Array[Byte](value.toByte, value.toByte, value.toByte)
    }
    override def decode[Int](byteArray: Array[Byte]) : Int = {
         byteArray.size   
    }
    override def check[Int](value:Int) : Boolean = {
        if (value > 0) true
        else false
    }
}

object Main extends App {
    val b = new TInt
    println(b.decode(Array[Byte]()))
}

// uncomment when compile
Main.main(args)

Unfortunately, I have the following error messages.

T.scala:11: error: value toByte is not a member of type parameter Int
        Array[Byte](value.toByte, value.toByte, value.toByte)
                          ^
T.scala:11: error: value toByte is not a member of type parameter Int
        Array[Byte](value.toByte, value.toByte, value.toByte)
                                        ^
T.scala:11: error: value toByte is not a member of type parameter Int
        Array[Byte](value.toByte, value.toByte, value.toByte)
                                                      ^
T.scala:14: error: type mismatch;
 found   : scala.Int
 required: Int(in method decode)
         byteArray.size
                   ^
T.scala:17: error: value > is not a member of type parameter Int
        if (value > 0) true
                  ^
5 errors found
  • Q1: How to improve RBase approach that uses Any and asInstanceOf?
  • Q2: What is wrong with TBase?

Solution

  • You were quite close with your idea. Parameterize the whole class instead of individual methods.

    abstract class TBase[T](val name: String) {
      def encode(value: T): Array[Byte]
      def decode(byteArray: Array[Byte]): T
      def check(value: T): Boolean
    }
    
    class TInt extends TBase[Int]("bit") {
      override def encode(value: Int) = {
        Array[Byte](value.toByte, value.toByte, value.toByte)
      }
      override def decode(byteArray: Array[Byte]): Int = {
        byteArray.size
      }
      override def check(value: Int): Boolean = {
        if (value > 0) true
        else false
      }
    }
    

    EDIT: To answer why your code doesn't work - because you parameterized the method with a type called "Int". Compiler thinks it's just a name for the type (it can be T, U, Int, Whatever). So the return type is not a "real Int" in that case, but your made-up type.