0

我想实现一个基于类型的 id 生成器,我可以这样调用:

val nextPersonId = idGen.id[Person]

我希望 IdGen 成为一个特征(如果可能的话):

trait IdGen[L <: HList] {
  def id[T] = {
    if (T is an element in the L type) return 0L + number of previous calls for this T
    else throw new RuntimeException("no such T in L")
  }
}

class MyDao extends IdGen[Person :: Booking :: Charges :: HNil] {
  //something needed here?
}

如何使用 Shapeless 实现这一点?

我尝试使用 scala 反射 typeTag 并迭代 toString 结果,但它很难看。

4

1 回答 1

0

好的,这是基于字符串的版本,它可能存在关于许多极端情况的错误,并且需要实现者以提供隐式 TypeTag 的形式生成样板:

import shapeless._

import scala.collection.mutable
import scala.reflect.runtime.universe._

trait IdGen[L <: HList] {
  implicit val ltag: TypeTag[L]
  private val idGens = mutable.Map(typeTag[L].toString.split("""::\[""").toList.tail.map(_.split(""",shapeless""")(0) -> 0L): _*)

  def id[T: TypeTag] = {
    val t = typeOf[T].toString
    val id = idGens(t)
    idGens(t) = id + 1L
    id
  }
}

简单的测试类:

import java.util.NoSuchElementException

import org.junit.{Assert, Test}
import shapeless._

import reflect.runtime.universe._

class Person

class Booking

class Charge

class MyDao(implicit val ltag: TypeTag[Person :: Booking :: HNil]) extends IdGen[Person :: Booking :: HNil]

class Testy {
  val myDao = new MyDao

  @Test(expected = classOf[NoSuchElementException])
  def test {
    Assert.assertEquals(0L, myDao.id[Person])
    Assert.assertEquals(1L, myDao.id[Person])
    Assert.assertEquals(0L, myDao.id[Booking])
    myDao.id[Charge]
  }
}

看到更好的实现会很有趣。

于 2016-04-18T08:31:47.687 回答