Home > Mobile >  Scala argument upper type bounds and overriding
Scala argument upper type bounds and overriding

Time:05-07

I am trying to understand and incorporate upper bound types with overriding in my system, but have not been able to achieve it without some ugly code. I have the following 2 traits:

trait MyId {
  def getType: Int // For simplicity. This is Enum/Object in code.
  def id: Any
}

trait MyTrait {
  protected def supportedType: Int
 
  def doSomething(id: MyId): Unit = {
    if (id.getType == myType) 
      doSomethingInternal(id)
  }
  
  protected def doSomethingInternal(id: _ <: MyId): Unit
}

and I want to create subtypes as:

class X(x: Long) extends MyId {
  override def getType: Int = 1
  override def id: Long = x
}

class Y(y: String) extends MyId {
  override def getType: Int = 2
  override def id: String = y
}

class A extends MyTrait {
  override protected def supportedType: Int = 1

  override protected def doSomethingInternal(id: X): Unit {...}
}

class B extends MyTrait {
  override protected def supportedType: Int = 2

  override protected def doSomethingInternal(id: Y): Unit {...}
}

However, this does not work. The workaround I have been using is to use asInstanceOf[]:

class A extends MyTrait {
  override protected def supportedType: Int = 1

  override protected def doSomethingInternal(id: MyId): Unit {
    val id2 = id.asInstanceOf[X]
  }
}

class B extends MyTrait {
  override protected def supportedType: Int = 2

  override protected def doSomethingInternal(id: MyId): Unit {
    val id2 = id.asInstanceOf[Y]
  }
}

Is there a way I can get rid of the asInstanceOf[]?

EDIT

Further more, I have the following processor which requires calling the correct MyTrait subclass based on the given MyId.getType:

def process(ids: Seq[MyId], processors: Map[Int, MyTrait]): Unit = {
  ids.foreach{ id =>
    processors.get(id.getType).doSomething(id)
  }
}

Edit 2:

  • getType is Int for simplicity here. This is actually an enum/object in my code.
  • Renamed getSomething to doSomething for more clarity.

CodePudding user response:

It's not clear from the question if there's a specific need for the indirection between getSomething and getSomethingInternal other than the getType consistency check, but since it looks like the goal is just to prevent calling getSomething with the wrong id type, it seems to me it'd be simpler to define MyTrait with a type parameter for the expected id:

trait MyTrait[T] {
  def getSomething(id: T): Unit
}
class A extends MyTrait[Long] {
  def getSomething(id: Long): Unit = { println(s"long id: $id") }
}
class B extends MyTrait[String] {
  def getSomething(id: String): Unit = { println(s"string id: $id") }
}

Then things look like this:

val a = A()
val b = B()

a.getSomething(123)
// prints "long id: 123"
b.getSomething("abc")
// prints "string id: abc"

a.getSomething("bad") // <--- doesn't compile
b.getSomething(123) // <--- doesn't compile

CodePudding user response:

It is not entirely clear to me what it is exactly that you are trying to do, but, perhaps, something like this will work. The key is to use Type for a type, and not attempt to encode it as an Int ... and then convert to a type.

trait MyId[Type] {
  def id: Type
}

trait MyTrait[T] {
  def getSomething(id: MyId[T]): Unit = 
      getSomethingInternal(id.id)
  
  
  protected def getSomethingInternal(id: T): Unit
}

Again, this was largely a shot in the dark, because I have no idea why your types are number or why your gets return Unit ... among other things.

Update Well, ok, if your type is an "enum", you wanna make your MyId a sealed trait:

   sealed trait MyId[T] {
      def id: T 
   }
   case class X(id: Long) extends MyId[Long]
   case class Y(id: String) extends MyId[String]

But now you want to process your ids uniformely. If the list of processors is static, just do match:

    def process(ids: Seq[MyId[_]]) = ids.map { 
       case x: X => processX
       case y: Y => process Y
    }

And if it isn't? Well .. in that case, I'd start with another upda te to your question, explaining why you need it, and how you generate that dynamic list.

  • Related