查看我的库的一些scala-docs,在我看来,值类中存在一些不需要的噪音。例如:
implicit class RichInt(val i: Int) extends AnyVal {
def squared = i * i
}
这会引入不需要的符号i
:
4.i // arghh....
这些东西出现在scala文档和IDE自动完成中,这实际上并不好。
那么......关于如何缓解这个问题的任何想法?我的意思是你可以使用RichInt(val self: Int)
,但这不会更好(4.self
,是吗?)
修改:
在下面的示例中,编译器是否擦除了中间对象?
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
}
答案 0 :(得分:5)
它确实会引入噪音(注意:在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
的副本?盒装。
但是这两种模式有效:
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
名称碰撞sorta无论如何都要处理你的问题。如果确实想要,您可以创建一个仅存在与repr
冲突的空值类。
有时您会在内部希望将您的值命名为比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
- 这可能是一个分割命名空间的好方法! - 而且你不必担心命名空间污染原repr
。
答案 1 :(得分:5)
在Scala 2.11中,您可以将val设为私有,从而解决了这个问题:
/mesos
答案 2 :(得分:3)
问题可能是绘制了值类的异构场景。来自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
,但不必)。
答案 3 :(得分:2)
可能是使用被遮蔽的名称:
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-docs中结束,但至少调用4.toInt
既不会混淆,也不会实际触发IntOps
。
答案 4 :(得分:0)
我不确定这是“不必要的噪音”,因为我认为在使用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