在泛型类型上过滤类型

时间:2018-04-10 10:30:26

标签: scala

我有以下代码(简化):

sealed trait Funding {
  id: Int
}

final case class FlatRate(id: Int, days: Int, amount: BigDecimal) extends Funding
final case class PerItem(id: Int, amount: BigDecimal, discount: BigDecimal) extends Funding

final case class AppliedFunding[+A <: Funding](
  person: String
)

我有一些代码会返回“通用”AppliedFunding列表,我希望得到一个具有正确类型的特定AppliedFunding。伪代码:

val allFunding: List[AppliedFunding[Funding]] = <...>
val flatrateFunding: List[AppliedFunding[FlatRate]] = allFunding.someMagicFunction

代替someMagicFunction我正在寻找一种方法来过滤掉正确的AppliedFunding个实例并以特定类型返回它们。我可以allFunding.collect { ... },但无论我尝试什么,它仍会返回List[AppliedFunding[Funding]]

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:3)

以下是2种方法。只是侧面标记:collectPartialFunction是好事的极少数情况之一

sealed trait Foo
final case class Bar(i:Int) extends Foo
final case class Baz(d:Double) extends Foo

final case class Container[F <: Foo](s:String, f:F) 


val all:List[Container[Foo]] = List(
  Container("bar", Bar(3)),
  Container("bar", Bar(4)),
  Container("bar", Bar(5)),
  Container("bar", Bar(6)),
  Container("bar", Bar(7)),
  Container("baz", Baz(1.5)),
  Container("baz", Baz(2.5)),
  Container("baz", Baz(3.5)),
  Container("baz", Baz(4.5)),
  Container("baz", Baz(5.5))
  )


val bars:List[Container[Bar]] = all.collect { case Container(s, Bar(i)) => Container(s, Bar(i))}
val baz:List[Container[Baz]] = all.foldRight(List.empty[Container[Baz]]) (
    (elem, lst) => elem match {
      case Container(s, Baz(d)) => Container(s, Baz(d)) :: lst
      case _ => lst
    }
  )

println(bars)
println(baz)

答案 1 :(得分:0)

以下是有关如何在isInstanceOfasInstanceOf的帮助下完成此操作的解决方案。另一个工作解决方案由Dominic在其他答案中提供,但是对于过滤用例,我不确定为每个通过过滤器的项创建一个新的case类实例是一种正确的方法。此外,使用scalac -print进行desugaring显示isInstanceOf调用在这种情况下不会进行任何操作,它将嵌入到生成的isDefinedAtapplyOrElse方法中,用于collectisInstanceOf方法中使用的部分函数{1}}。

有些人说使用asInstanceOfcollect是一种代码味道,但对我而言,在这种特殊情况下,使用是安全和正确的,并且不会带来{{1调用collect只是在生成的源代码中隐藏isInstanceOf并实例化新的案例类实例是对过滤集合上asInstanceOf的安全调用的权衡。

另请注意,我已在funding案例类中添加了AppliedFunding字段。我认为,AppliedFunding个实例没有关于资金本身的嵌入信息,无论如何都不是很有用。

object Test {
  sealed trait Funding {
    val id: Int
  }

  final case class FlatRate(id: Int, days: Int, amount: BigDecimal) extends Funding
  final case class PerItem(id: Int, amount: BigDecimal, discount: BigDecimal) extends Funding

  final case class AppliedFunding[+A <: Funding](
    person: String, funding: A)

  val allFundings: List[AppliedFunding[Funding]] = List(AppliedFunding[FlatRate]("alex", FlatRate(1, 10, 100)), AppliedFunding[PerItem]("christian", PerItem(2, 100, 10)))

  def flatRatePredicate[A <: Funding](appliedFunding: AppliedFunding[A]): Boolean = appliedFunding.funding.isInstanceOf[FlatRate]

  val flatRateFundings: List[AppliedFunding[FlatRate]] = allFundings.filter(flatRatePredicate).asInstanceOf[List[AppliedFunding[FlatRate]]]

  def main(args: Array[String]): Unit = {
    println(flatRateFundings)
  }
}