Как компилятор Scala обрабатывает конкретные методы черт? - PullRequest
10 голосов
/ 14 августа 2011

Если у меня есть следующий класс Scala:

abstract class MyOrdered extends Ordered[MyOrdered] {
    def id: Int
    def compare(that : MyOrdered) : Int =
        if (that==null) 1 else (id-that.id)
}

Тогда мне нужно только определить метод id в Scala, чтобы получить конкретный класс.Но если я попытаюсь расширить его в Java , компилятор скажет, что отсутствуют все конкретные методы Ordered.Значит ли это, что компилятор Scala помещает реализацию конкретных методов Ordered только в конкретные классы Scala?

Это кажется очень расточительным, потому что я мог бы иметь десятки конкретных классов, реализующих MyOrdered, и они бывсе получают копию одного и того же кода, хотя на самом деле было бы достаточно просто поместить его непосредственно в базовый класс MyOrdered.Кроме того, это очень затрудняет создание Java Scala API, дружественного к Java.Есть ли способ заставить компилятор Scala поместить определения метода туда, где он должен был это сделать, в любом случае , кроме как сделать класс конкретным, используя реализации фиктивных методов?

Даже более смешная декларацияконкретный метод финала в черте Scala.В этом случае все еще не реализован в абстрактном классе Scala, который расширяет черту, но он не может быть реализован в классе Java, который расширяет абстрактный класс Scala, потому что он помечен как финальный.Это определенно ошибка компилятора.Конечные абстрактные методы не имеют смысла, даже если они допустимы в JVM, по-видимому.

Ответы [ 2 ]

11 голосов
/ 14 августа 2011

Scala 2.9.1.RC1

Позвольте представить вам нашего друга :javap в REPL, который может быть полезен для диагностики ошибок. Сначала мы определим класс,

scala> abstract class MyOrdered extends Ordered[MyOrdered] {
     |     def id: Int
     |     def compare(that : MyOrdered) : Int =
     |         if (that==null) 1 else (id-that.id)
     | }
defined class MyOrdered

А затем попросите увидеть байт-код JVM,

scala> :javap -v MyOrdered
Compiled from "<console>"
public abstract class MyOrdered extends java.lang.Object implements scala.math.Ordered,scala.ScalaObject

...
** I'm skipping lots of things here: $less, $lessEq, ... **
...

public boolean $greater(java.lang.Object);
  Code:
   Stack=2, Locals=2, Args_size=2
   0:   aload_0
   1:   aload_1
   2:   invokestatic    #19; //Method scala/math/Ordered$class.$greater:(Lscala/math/Ordered;Ljava/lang/Object;)Z
   5:   ireturn
  LineNumberTable: 
   line 7: 0

...

public abstract int id();

public int compare(MyOrdered);
  Code:
   Stack=2, Locals=2, Args_size=2
   0:   aload_1
   1:   ifnonnull   8
   4:   iconst_1
   5:   goto    17
   8:   aload_0
   9:   invokevirtual   #38; //Method id:()I
   12:  aload_1
   13:  invokevirtual   #38; //Method id:()I
   16:  isub
   17:  ireturn
  LineNumberTable: 
   line 10: 0

 ...

Мы видим, что скаляр на самом деле генерирует методы в MyOrdered, соответствующие тем конкретным в черте Ordered. Например, метод > переводится в $greater и в основном просто вызывает scala/math/Ordered$class.$greater. Если мы хотим, мы можем теперь искать байт-код для конкретных определений черт,

scala> :javap -v scala.math.Ordered$class
Compiled from "Ordered.scala"
public abstract class scala.math.Ordered$class extends java.lang.Object
...
public static boolean $greater(scala.math.Ordered, java.lang.Object);
  Code:
   Stack=2, Locals=2, Args_size=2
   0:   aload_0
   1:   aload_1
   2:   invokeinterface #12,  2; //InterfaceMethod scala/math/Ordered.compare:(Ljava/lang/Object;)I
   7:   iconst_0
   8:   if_icmple   15
   11:  iconst_1
   12:  goto    16
   15:  iconst_0
   16:  ireturn
  LineNumberTable: 
   line 46: 0
...

Наконец, давайте проверим вашу гипотезу о том, что подкласс M из MyOrdered получает полную копию всех методов

scala> class M extends MyOrdered { def id = 2 }
defined class M

scala> :javap -v M
Compiled from "<console>"
public class M extends MyOrdered implements scala.ScalaObject
....
** No extra methods besides id **
....

Нет, похоже, здесь нет дублирования кода.

В заключение,

  • Scalac немного творит магию с помощью конкретных методов, поэтому не пытайтесь наследовать их от Java. Абстрактные классы должны быть в порядке.

  • JVM изначально не поддерживает имена символических методов, одноэлементные объекты Scala и черты с конкретными методами, поэтому компилятору Scala необходимо выполнить некоторый перевод и использовать зарезервированный символ $.

Если у вас все еще есть проблемы с взаимодействием Java, надеюсь, :javap поможет вам в диагностике конкретной проблемы.

0 голосов
/ 23 августа 2016

Примечание: последний коммит для Scal 2.12.x (август 2016) может немного оптимизировать вещи в compiler/scala/tools/nsc/backend/jvm:

SD-192 Изменить схему для черты superaccessors

Вместо того, чтобы помещать код тела метода trait в статический метод, оставьте его в методе по умолчанию.
Статический метод (необходимый в качестве цели супер-вызовов) теперь использует invokespecialточно вызвать этот метод.

...