首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >具有多类型参数的Scala case类

具有多类型参数的Scala case类
EN

Stack Overflow用户
提问于 2016-10-05 01:56:41
回答 3查看 2.5K关注 0票数 1

我需要检查嵌套模式的完整性,因此我编写了case类来做这件事。我面临的主要障碍是模式可能有一个StringUtf8类型的字段(比方说name),我想同时接受这两个实例。是否可以避免将两个case类设置为

代码语言:javascript
复制
 case class NameValueString(name: String, value: Double)
 case class NameValueUtf8(name: Utf8, value: Double)

像这样的东西

代码语言:javascript
复制
 case class NameValue(name @(_:String | _:Utf8), value: Double)

上面的表达式肯定无法编译。

尼基尔

EN

回答 3

Stack Overflow用户

发布于 2016-10-05 02:32:34

一种方法是所谓的类型类:

代码语言:javascript
复制
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中获得的任何通用功能。

你可以根据证据进行匹配:

代码语言:javascript
复制
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

票数 4
EN

Stack Overflow用户

发布于 2016-10-05 03:16:26

另一种模式(不需要隐式参数):

代码语言:javascript
复制
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的回答来看待完整的类型类方法:

代码语言:javascript
复制
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)
//                ^

UPDATE2

再来一个(使用union type实现类型安全):

代码语言:javascript
复制
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
  } 
票数 2
EN

Stack Overflow用户

发布于 2016-10-05 06:19:29

因为你已经在使用case类了,如果你只需要不同的方式来创建它,并且你可以只用一种方式来表示你的数据,你可以添加你自己的apply方法来使用不同的参数来实现创建。

代码语言:javascript
复制
 case class NameValue(name: String, value: Double)
 object NameValue{
     def apply(name: Utf8, value: Double): NameValue = {
         new NameValue( name.toString, value )
     }
 }

或者,如果你想要模式匹配并从不同的选项中提取NameValue,你可能需要选中提取器,这基本上就是创建你自己的取消应用方法...检查http://danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39858823

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档