简体   繁体   中英

How to Provide Specialized Implementations of Generic Methods in Scala?

I'm working on an existing code base with a wrapper class around Slick 2.1.0 (I know). This wrapper has a method named transaction that is a generic - it takes a (f: => T) (so it's pass-by-name). I need to mock this class for an unit test. We're also using Mockito 1.10.19 (again, I know), which won't let me mock a pass-by-name (I believe...). So I'm stuck implementing the underlying trait that this wrapper class is built on.

The immediate problem is this: I want to mock this transaction method so it does nothing. The code I'm testing passes in a (f: => Unit) . So I want to implement this method to return a Future.Done . (Did I mention we're using Finagle and not Scala futures?) But this method is generic. How do I properly specialize?

This is my current attempt:

val mockDBM = new DatabaseManager {
    override def transaction[@specialized(Unit) T](f: => T): Future[T] = Future.value(f)
    def transaction(f: => Unit): Future[Unit] = Future.Done
}

Of course, I get a have same type after erasure error upon compilation. Obviously I have no idea how @specialized works.

What do I do? Maybe I can use Mockito after all? Or I need to learn what specializing a generic method actually means?

I found this, which probably contains the answer, but I have no formal background in FP, and I don't grok this at all: How can one provide manually specialized implementations with Scala specialization?

@specialized doesn't let you provide specializations, it just generates its own. The answer provided in the linked question would require changing the signature. From the question it looks like you can't change it, in which case you are out of luck. If you can... you may still be out of luck, depending on how exactly this code is going to be called.

OTOH, the solution for "I want to disregard f , but can only return Future.Done if the generic is for a Unit type" is far simpler:

class Default[A] {
  var x: A = _
}
object Default {
  def apply[A]() = (new Default[A]).x
}

val mockDBM = new DatabaseManager {
  override def transaction[T](f: => T): Future[T] = {
    Future.value(Default(x))
  }
}

Assuming you need a successful future, but don't care about value, that is; if you just need any future, override def transaction[T](f: => T): Future[T] = Future.??? .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM