查看我的库的一些scala文档,我发现值类中存在一些不需要的噪声。例如:
implicit class RichInt(val i: Int) extends AnyVal {
def squared = i * i
}
这引入了一个不需要的符号i
:
4.i // arghh....
这些东西出现在scala文档和IDE自动完成中,这真的不好。
所以。。。关于如何缓解这个问题,有什么想法吗?我的意思是你可以使用RichInt(val self: Int)
,但这并不能让它变得更好(4.self
,wth?)
编辑:
在以下示例中,编译器是否擦除中间对象?
import language.implicitConversions
object Definition {
trait IntOps extends Any { def squared: Int }
implicit private class IntOpsImpl(val i: Int) extends AnyVal with IntOps {
def squared = i * i
}
implicit def IntOps(i: Int): IntOps = new IntOpsImpl(i) // optimised or not?
}
object Application {
import Definition._
// 4.i -- forbidden
4.squared
}
在Scala 2.11中,您可以将val设为私有,从而修复了这个问题:
implicit class RichInt(private val i: Int) extends AnyVal {
def squared = i * i
}
它确实引入了噪声(注意:在2.10中,在2.11及更高版本中,您只需声明val私有)。你并不总是想。但现在就是这样。
你不能通过遵循私有值类模式来解决这个问题,因为编译器实际上看不到它的末尾是一个值类,所以它通过泛型路径。这是字节码:
12: invokevirtual #24;
//Method Definition$.IntOps:(I)LDefinition$IntOps;
15: invokeinterface #30, 1;
//InterfaceMethod Definition$IntOps.squared:()I
看看第一个如何返回类Definition$IntOps
的副本?它是盒装的。
但这两种模式起作用,有点像:
(1) 通用名称模式
implicit class RichInt(val repr: Int) extends AnyVal { ... }
implicit class RichInt(val underlying: Int) extends AnyVal { ... }
使用其中一个。添加i
作为一种方法很烦人。在没有任何基础的情况下添加underlying
并没有那么糟糕——只有当你试图获得基础值时,你才会成功。如果你一次又一次地使用同一个名字:
implicit class RicherInt(val repr: Int) extends AnyVal { def sq = repr * repr }
implicit class RichestInt(val repr: Int) extends AnyVal { def cu = repr * repr * repr }
scala> scala> 3.cu
res5: Int = 27
scala> 3.repr
<console>:10: error: type mismatch;
found : Int(3)
required: ?{def repr: ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method RicherInt of type (repr: Int)RicherInt
and method RichestInt of type (repr: Int)RichestInt
名称冲突排序无论如何都会解决您的问题。如果真的想要,可以创建一个空值类,该类的存在只是为了与repr
冲突。
(2) 显式-隐式模式
有时,您在内部希望将值命名为比repr
或underlying
更短或更便于记忆的名称,而不使其在原始类型上可用。一种选择是创建一个隐含的转发,如下所示:
class IntWithPowers(val i: Int) extends AnyVal {
def sq = i*i
def cu = i*i*i
}
implicit class EnableIntPowers(val repr: Int) extends AnyVal {
def pow = new IntWithPowers(repr)
}
现在您必须调用3.pow.sq
而不是3.sq
——这可能是分割命名空间的好方法--并且您不必担心原始CCD_ 12之外的命名空间污染。
也许问题在于绘制值类的异构场景。来自SIP:
•内嵌隐式包装。这些包装器上的方法将被转换为扩展方法。
•新的数字类,如无符号整数。这样的类将不再需要拳击开销。所以这类似于.NET.中的值类
•代表度量单位的类。同样,这些类不会产生装箱开销。
我认为前两者和后两者有区别。在第一种情况下,值类本身应该是透明的。您不会期望在任何地方都有类型RichInt
,但实际上您只在Int
上操作。在第二种情况下,例如4.meters
,我理解获得实际的"值"是有意义的,因此需要val
是可以的
这种分裂再次反映在价值类别的定义中:
1.C必须只有一个参数,该参数用val标记,并且具有公共可访问性。
7.C必须是短暂的。
后者意味着它没有其他字段等,与1相矛盾。
带
class C(val u: U) extends AnyVal
SIP中唯一使用u
的地方是示例实现(例如def extension$plus($this: Meter, other: Meter) = new Meter($this.underlying + other.underlying)
);然后在中间表示中,最后只被再次擦除:
new C(e).u ⇒ e
合成方法IMO可访问的中间表示也可以由编译器完成,但在用户编写的代码中不应可见。(即,如果您想访问对等设备,可以使用val
,但不必)。
一种可能性是使用阴影名称:
implicit class IntOps(val toInt: Int) extends AnyVal {
def squared = toInt * toInt
}
或
implicit class IntOps(val toInt: Int) extends AnyVal { ops =>
import ops.{toInt => value}
def squared = value * value
}
这仍然会在scala文档中结束,但至少调用4.toInt
既不会令人困惑,也不会实际触发IntOps
。
我不确定这是"不需要的噪音",因为我认为在使用RichInt
时,您几乎总是需要访问底层值。考虑一下:
// writing ${r} we use a RichInt where an Int is required
scala> def squareMe(r: RichInt) = s"${r} squared is ${r.squared}"
squareMe: (r: RichInt)String
// results are not what we hoped, we wanted "2", not "RichInt@2"
scala> squareMe(2)
res1: String = RichInt@2 squared is 4
// we actually need to access the underlying i
scala> def squareMeRight(r: RichInt) = s"${r.i} squared is ${r.squared}"
squareMe: (r: RichInt)String
此外,如果您有一个添加两个RichInt
的方法,则需要再次访问底层值:
scala> implicit class ImplRichInt(val i: Int) extends AnyVal {
| def Add(that: ImplRichInt) = new ImplRichInt(i + that) // nope...
| }
<console>:12: error: overloaded method value + with alternatives:
(x: Int)Int <and>
(x: Char)Int <and>
(x: Short)Int <and>
(x: Byte)Int
cannot be applied to (ImplRichInt)
def Add(that: ImplRichInt) = new ImplRichInt(i + that)
^
scala> implicit class ImplRichInt(val i: Int) extends AnyVal {
| def Add(that: ImplRichInt) = new ImplRichInt(i + that.i)
| }
defined class ImplRichInt
scala> 2.Add(4)
res7: ImplRichInt = ImplRichInt@6