值类引入了不需要的公共方法



查看我的库的一些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) 显式-隐式模式

有时,您在内部希望将值命名为比reprunderlying更短或更便于记忆的名称,而不使其在原始类型上可用。一种选择是创建一个隐含的转发,如下所示:

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

相关内容

  • 没有找到相关文章

最新更新