Вносит ли наследование в неявные классы значений накладные расходы?
Я хочу применить классы значений scala к одному из моих проектов, потому что они позволяют мне обогащать определенные примитивные типы без больших накладных расходов (я надеюсь) и оставаться безопасными для типов.
object Position {
implicit class Pos( val i: Int ) extends AnyVal with Ordered[Pos] {
def +( p: Pos ): Pos = i + p.i
def -( p: Pos ): Pos = if ( i - p.i < 0 ) 0 else i - p.i
def compare( p: Pos ): Int = i - p.i
}
}
Мой вопрос: будет ли наследство Ordered
заставить распределение Pos
объекты всякий раз, когда я их использую (тем самым вводить большие накладные расходы) или нет? Если так: есть ли способ обойти это?
1 ответ
Каждый раз Pos
будет рассматриваться как Ordered[Pos]
, распределение произойдет. Есть несколько случаев, когда должно произойти распределение, см. http://docs.scala-lang.org/overviews/core/value-classes.html.
Поэтому, когда вы делаете что-то простое, например, <
Вы получите отчисления:
val x = Pos( 1 )
val y = Pos( 2 )
x < y // x & y promoted to an actual instance (allocation)
Соответствующие правила (цитаты из вышеуказанной статьи):
Всякий раз, когда класс значения обрабатывается как другой тип, включая универсальную черту, должен создаваться экземпляр экземпляра класса фактического значения и: Еще один экземпляр этого правила - когда класс значения используется в качестве аргумента типа.
Разборка приведенного выше фрагмента кода подтверждает это:
0: aload_0
1: iconst_1
2: invokevirtual #21 // Method Pos:(I)I
5: istore_1
6: aload_0
7: iconst_2
8: invokevirtual #21 // Method Pos:(I)I
11: istore_2
12: new #23 // class test/Position$Pos
15: dup
16: iload_1
17: invokespecial #26 // Method test/Position$Pos."<init>":(I)V
20: new #23 // class test/Position$Pos
23: dup
24: iload_2
25: invokespecial #26 // Method test/Position$Pos."<init>":(I)V
28: invokeinterface #32, 2 // InterfaceMethod scala/math/Ordered.$less:(Ljava/lang/Object;)Z
Как видно, у нас есть два экземпляра "нового" кода операции для класса Position$Pos
ОБНОВЛЕНИЕ: чтобы избежать выделения в таких простых случаях, как этот, вы можете вручную переопределить каждый метод (даже если они только пересылают в исходную реализацию):
override def < (that: Pos): Boolean = super.<(that)
override def > (that: Pos): Boolean = super.>(that)
override def <= (that: Pos): Boolean = super.<=(that)
override def >= (that: Pos): Boolean = super.>=(that)
Это удалит распределение при выполнении x < y
по примеру. Тем не менее, это все еще оставляет случаи, когда Pos
рассматривается как Ordered[Pos]
(как при передаче в метод, принимающий Ordered[Pos]
или Ordered[T]
с Т является параметром типа). В этом конкретном случае вы все равно получите распределение, и нет никакого способа обойти это.