Вносит ли наследование в неявные классы значений накладные расходы?

Я хочу применить классы значений 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] с Т является параметром типа). В этом конкретном случае вы все равно получите распределение, и нет никакого способа обойти это.

Другие вопросы по тегам