假设我想要一个类似Java Date
的类。它唯一的数据成员是一个long,表示自1970年以来的毫秒数。
只是制作新的Scala类型会有什么性能优势:
type PrimitiveDate = Long
然后,您可以使用隐式转换添加方法,就像使用RichInt
对int进行的那样。原始类型的这种“拳击”是否涉及任何开销(类创建)?基本上你可以有一个静态方法
def addMonth(date: PrimitiveDate, months: Int): PrimitiveDate = date + 2592000000 * months
让类型系统弄清楚它必须在d addMonth 5
时应用
出现在您的代码中。
修改
scala编译器似乎没有强制通过编写type PrimitiveDate = Long
创建的别名。创建一个合适的类,包含Long,这是在Scala中创建强制类型的唯一方法吗?
您认为能够为基本类型创建强制类型别名有多大用处?
答案 0 :(得分:11)
嗯, escape analysis 应该意味着最新的JVM实际上不必创建丰富的包装来调用addMonth
方法。
实际上在实践中发生的程度显然取决于JVM决定这些方法在对象创建中添加多少运行时热点。当没有进行转义分析时,显然JVM必须在包装类的新实例中“装箱”(如你所说)Long
。它不涉及“类创建” - 它将涉及“创建类的实例”。这个短暂的实例将立即成为GC-d,因此开销(虽然很小)是:
如果您正在编写非常低延迟的代码,而您正在尝试最小化垃圾创建(在紧密循环中),这些显然只会出现任何问题。只有你知道是否是这种情况。
至于这种方法是否对您有效(并且逃避分析对您有帮助),您必须在野外进行测试。众所周知,微观基准很难为这类事情写作。
我不太喜欢这些类型别名是公共API 的一部分的原因是scala并没有像我希望的那样严格执行它们。例如:
type PrimitiveDate = Long
type PrimitiveSpeed = Long
type Car = String
type Meeting = String
var maxSpeeds : Map[Car, PrimitiveSpeed] = Map.empty
//OOPS - much too easy to accidentally send the wrong type
def setDate(meeting : Meeting, date : PrimitiveDate) = maxSpeeds += (meeting -> date)
答案 1 :(得分:4)
您实际上并未在给定示例中创建新类型,它只是预先存在的Long类型的别名。
这是我经常使用的一种技术来处理笨重的嵌套连接。例如,我使用别名type Grid = Seq[Seq[Int]]
来避免必须一遍又一遍地为各种参数指定Seq[Seq[Int]]
。
您可以非常愉快地将Long
传递给采用PrimitiveDate
方法的方法,尽管执行的优势在于代码可以更好地自我记录。< / p>
如果你真的想创建一个强制类型安全和方便模式匹配的新类型,我会使用一个案例类:
case class PrimitiveDate(value:Long)
并且,为方便起见,甚至可能提供隐式的Long =&gt; PrimitiveDate转换。
答案 2 :(得分:3)
一年后,他向more polished and robust version添加了Shapeless。这个概念简单而简洁,可以在不添加Shapeless的情况下复制到项目中,如果你不想要那个优秀的库的其余部分。
当然,你和回答你问题的人都知道这一点,但值得在这里补充,因为这仍然是一个重要的问题。