簡體   English   中英

具有多類型參數的Scala案例類

[英]Scala case class with multiple-type argument

我需要檢查嵌套模式的完整性,因此我正在編寫案例類來這樣做。 我面臨的主要障礙是模式可能有一個StringUtf8類型的字段(比如name ),我想接受這兩個實例。 是否有可能避免將兩個案例類作為

 case class NameValueString(name: String, value: Double)
 case class NameValueUtf8(name: Utf8, value: Double)

和類似的東西

 case class NameValue(name @(_:String | _:Utf8), value: Double)

上面的表達式肯定無法編譯。

尼基爾

一種方法是所謂的類型類:

trait StringLike[A] // sealed if you don't want anybody to implement it elsewhere
object StringLike {
  implicit object StringEv extends StringLike[String] {}
  implicit object Utf8Ev extends StringLike[Utf8] {}
}

case class NameValue[A](name: A, value: Double)(implicit val stringLike: StringLike[A])

當然, StringLike通常不會為空,而是從StringUtf8描述您需要的任何通用功能。

您可以匹配證據:

def nameLength[A](nameValue: NameValue[A]) = nameValue.stringLike match {
  case StringLike.StringEv => 
    nameValue.name.length // calls String#length
  case StringLike.Utf8Ev =>
    nameValue.name.length // calls Utf8#length (assuming Utf8 has such method)
}

在這種情況下,編譯器甚至會知道A (以及nameValue.name的類型)在第一個分支中是String ,在第二個分支中是Utf8

另一種模式(不需要隱式參數):

import scala.language.implicitConversions

class StringLike[A](name: A) {
  override def toString = {
    name match {
      case s: String => s"String: $s"
      case i: Int => s"Int: $i"
    }
  }
}
implicit def string2StringLike(s: String) = new StringLike(s)
implicit def int2StringLike(i: Int) = new StringLike(i)

case class NameValue[A](name: StringLike[A], value: String) {
  override def toString = name.toString
}

NameValue("123", "123")
//> NameValue[String] = String: 123
NameValue(13, "123")
//> NameValue[Int] = Int: 13
NameValue(13.9, "123")
// error: type mismatch;
//  found   : Double(13.9)
//  required: StringLike[?]
//      NameValue(13.9, "123")
//                ^

更新

以下是我如何看待基於 Alexey 的回答完成的類型類方法:

trait StringLike[A] {
  def toString(x: A): String
}

object StringLike {
  implicit object StringStringLike extends StringLike[String] {
    def toString(s: String) = s"String: $s"
  }
  implicit object IntStringLike extends StringLike[Int] {
    def toString(i: Int) = s"Int: $i"
  } 
}

import StringLike._

case class NameValue[A](name: A, value: Double)(implicit ev: StringLike[A]) {
  override def toString = ev.toString(name)
}

NameValue(1, 2.0)
//> NameValue[Int] = Int: 1

NameValue("123", 2.0)
//> NameValue[String] = String: 123

NameValue(2.0, 2.0)
// error: could not find implicit value for parameter ev: 
// StringLike[Double]
//       NameValue(2.0, 2.0)
//                ^

更新2

再一個(使用聯合類型來保證類型安全):

type ¬[A] = A => Nothing
type ¬¬[A] = ¬[¬[A]]
type ∨[T, U] = ¬[¬[T] with ¬[U]]
type |∨|[T, U] = { type λ[X] = ¬¬[X] <:< (T ∨ U) }

def nameLength[A: ClassTag: (Int |∨| String)#λ](nameValue: NameValue[A]) =
  nameValue.name match {
    case s:String => s.length
    case i:Int => i + 1
  } 

由於您已經在使用案例類,如果您只需要不同的方式來創建它,並且您可以僅以一種方式表示數據,那么您可以添加自己的 apply 方法以使用不同的參數啟用創建。

 case class NameValue(name: String, value: Double)
 object NameValue{
     def apply(name: Utf8, value: Double): NameValue = {
         new NameValue( name.toString, value )
     }
 }

或者,如果您想模式匹配並從不同的選項中提取 NameValue,您可能需要檢查 Extractors,這基本上是創建您自己的未應用方法...檢查http://danielwestheide.com/blog/2012/11/21 /the-neophytes-guide-to-scala-part-1-extractors.html

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM