Scala在列表中仅接受String或Int通用案例类

时间:2018-10-28 06:42:03

标签: java scala

我有一个如下定义的案例类

case class ChooseBoxData[T](index:T, text:String)

是否可以声明一个列表,以便该列表仅接受ChooseBoxData [String]和ChooseBoxData [Int]的类型?

我期望的是这样的:

val specialList:List[some type declaration] = List(
  ChooseBoxData[String]("some string","some string"),/* allow, because is ChooseBoxData[String]*/
  ChooseBoxData[Int](12,"some string"), /* also allow, because is ChooseBoxData[Int]*/
  ChooseBoxData[Boolean](true,"some string")/* not allow type other than ChooseBoxData[String] or ChooseBoxData[Int]*/
)

4 个答案:

答案 0 :(得分:1)

这是我想出的:

首先,我们创建以下代数数据类型(ADT):

sealed trait StringInt

case class Stringy(s : String) extends StringInt
case class Inty(s : Int) extends StringInt

并如下定义ChoooseBoxData

case class ChooseBoxData(index : StringInt, text : String)

然后,我们定义以下隐式将范围内的IntString转换为已定义的ADT:

object CBImplicits {
  implicit def conv(u : String) = Stringy(u)
  implicit def conv2(u : Int) = Inty(u)
}

现在,我们可以在问题中强制执行要求。这是一个示例:

import CBImplicits._

val list = List(ChooseBoxData("str", "text"),
ChooseBoxData(1, "text"),
ChooseBoxData(true, "text"))

尝试运行以上命令,编译器将抱怨类型不匹配。但这将编译并运行:

List(
  ChooseBoxData("str", "text"),
  ChooseBoxData(1, "text"),
  ChooseBoxData(12, "text2"))

结果为:

a: List[ChooseBoxData] = 
List(ChooseBoxData(Stringy(str),text), ChooseBoxData(Inty(1),text), ChooseBoxData(Inty(12),text2))

这保留了index类型信息(当然是StringInt超类型包装的信息),以后可以使用针对各个元素的模式匹配轻松提取出这些信息。

也很容易删除所有元素的包装,但是这将导致index类型变为Any,这是我们期望的,因为Any是最常见的Scala类层次结构中StringInt的祖先。

编辑:使用Shapeless

的解决方案
import shapeless._
import syntax.typeable._

case class ChooseBoxData[T](index : T, text : String)

val a = ChooseBoxData(1, "txt")
val b = ChooseBoxData("str", "txt")
val c = ChooseBoxData(true, "txt")
val list = List(a, b, c)

val `ChooseBoxData[Int]` = TypeCase[ChooseBoxData[Int]]
val `ChooseBoxData[String]` = TypeCase[ChooseBoxData[String]]

val res = list.map {
  case `ChooseBoxData[Int]`(u) => u
  case `ChooseBoxData[String]`(u) => u
  case _ => None
}
//result
res: List[Product with Serializable] = List(ChooseBoxData(1,txt), ChooseBoxData(str,txt), None)

因此它允许编译,但是将用None替换无效的实例(如果需要,可以将其用于引发运行时错误),或者您可以使用以下方法直接过滤所需的实例:

list.flatMap(x => x.cast[ChooseBoxData[Int]])
//results in:
List[ChooseBoxData[Int]] = List(ChooseBoxData(1,txt))

答案 1 :(得分:1)

类似这样的东西:

trait AllowableBoxData  
object AllowableBoxData {
private of[T](cbd: ChooseBoxData[T]) = new ChooseBoxData(cbd.index, cbd.text) 
    with AllowableBoxData
  implicit def ofInt(cbd: ChooseBoxData[Int]) = of(cbd)
  implicit def ofString(cbd: ChooseBoxData[String]) = of(cbd)
}

现在您可以做

之类的事情
val list: List[ChooseBoxData[_] with AllowableBoxData] = List(ChooseBoxData("foo", "bar"), ChooseBoxData(0, "baz")

但不是val list: List[AllowableBoxData] = List(ChooseBoxData(false, "baz"))

此外,如果您要声明一个函数参数而不是一个变量,那么会有一个更优雅的解决方案:

trait CanUse[T]
implicit case object CanUseInt extends CanUse[Int]
implicit case object CanUseString extends CanUse[String]

def foo[T : CanUse](bar: List[ChooseBoxData[T]])

答案 2 :(得分:0)

您可以在案例类的顶部构建额外的约束。

import language.implicitConversions

case class ChooseBoxData[T](index:T, text:String)

trait MySpecialConstraint[T] {
  def get: ChooseBoxData[T]
}

implicit def liftWithMySpecialConstraintString(cbd: ChooseBoxData[String]) =
  new MySpecialConstraint[String] {
    def get = cbd
  }

implicit def liftWithMySpecialConstraintInt(cbd: ChooseBoxData[Int]) =
  new MySpecialConstraint[Int] {
    def get = cbd
  }


// Now we can just use this constraint for out list 
val l1: List[MySpecialConstraint[_]] = List(ChooseBoxData("A1", "B1"), ChooseBoxData(2, "B2"))

答案 3 :(得分:0)

为什么不能这样:

object solution extends App {

  case class ChooseBoxData[T](index: T, text: String) extends GenericType[T]

  trait GenericType[T] {
    def getType(index: T, text: String): ChooseBoxData[T] = ChooseBoxData[T](index, text)
  }

  val specialList = List(
    ChooseBoxData[String]("some string", "some string"),
    ChooseBoxData[Int](12, "some string"), 
    ChooseBoxData[Boolean](true, "some string")
  )

  println(specialList)
}

//output: List(ChooseBoxData(some string,some string), ChooseBoxData(12,some string), ChooseBoxData(true,some string))