简体   繁体   中英

Scala, override method on generic type

I am trying to build a DSL , one of the method on this DSL is parameterless and use a bounded generic type. Today I have to add a "feature" that will ideally use the same method name. However, because the only parameter is the generic one, I cannot override it with the usual way.

Is there a trick to allow the use of the same method for different generic types ?

My method looks like:

def ask[H <: Handler] = {
  new CommandBuilder[H]
}
class CommandBuilder[H <: Handler] {
  def toExecute[C <: H#C](command: C) = {
    //...
  }
} 

And I would like to add:

def ask[S <: State] = {
  new QueryBuilder[S]
}
class QueryBuilder[S <: State] {
  def toExecute[Q <: S#Q](query: Q) = {
    //...
  }
} 

I was thinking to pattern match a ClassTag on the type but I need strong type safety:

  • Query on a Handler , is not allowed. ask[State] must return QueryBuilder
  • Command and Query are the only supported types. The generic type of ask can only be a Handler or a State .

Maybe you could refactor your code to something like this?

sealed trait FooBar

sealed trait Foo extends FooBar {
  def process(i: Int): Int
}

object Foo {
  implicit final case object FooImpl extends Foo {
    override def process(i: Int): Int = i + 1
  }
}

sealed trait Bar extends FooBar {
  def process(s: String): String
}

object Bar {
  implicit final case object BarImpl extends Bar {
    override def process(s: String): String = s.toUpperCase
  }
}

object Test {
  trait FooBarPartiallyApplied[FB <: FooBar] {
    type Out
    def out: Out
  }

  object FooBarPartiallyApplied {
    type Aux[FB <: FooBar, _Out] = FooBarPartiallyApplied[FB] { type Out = _Out }

    implicit final def FooPartiallyAppliedBuilder[F <: Foo]: Aux[F, FooPartiallyApplied[F]] =
      new FooBarPartiallyApplied[F] {
        override final type Out = FooPartiallyApplied[F]

        override final val out: FooPartiallyApplied[F] =
          new FooPartiallyApplied[F](dummy = true)
      }

    implicit final def BarPartiallyAppliedBuilder[B <: Bar]: Aux[B, BarPartiallyApplied[B]] =
      new FooBarPartiallyApplied[B] {
        override final type Out = BarPartiallyApplied[B]

        override final val out: BarPartiallyApplied[B] =
          new BarPartiallyApplied[B](dummy = true)
      }

    final class FooPartiallyApplied[F <: Foo](private val dummy: Boolean) extends AnyVal {
      def toExecute(i: Int)(implicit foo: F): Int = foo.process(i)
    }

    final class BarPartiallyApplied[B <: Bar](private val dummy: Boolean) extends AnyVal {
      def toExecute(s: String)(implicit bar: B): String = bar.process(s)
    }
  }

  def ask[FB <: FooBar](implicit pa: FooBarPartiallyApplied[FB]): pa.Out =
    pa.out
}

It works as expected:

Test.ask[Foo.FooImpl.type].toExecute(10)
// res: Int = 11

Test.ask[Foo.FooImpl.type].toExecute("blah")
// Type error.

Test.ask[Bar.BarImpl.type].toExecute(10)
// Type error.

Test.ask[Bar.BarImpl.type].toExecute("blah")
// res: String = "BLAH"

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