Почему классы неявных значений имеют дополнительный вызов метода?
Я проверял байт-код, генерируемый неявными классами, и хотел сравнить с тем, что генерируется при расширении AnyVal
,
Без неявного:
object Example1 {
class Wrapper(val self: Int) extends AnyVal {
def add(n: Int): Int = self + n
}
def foo(w: Wrapper): Wrapper = new Wrapper(w.add(42))
}
(Соответствующая часть) байт-код:
scala>:javap Example1
[...]
public int foo(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
0: getstatic #19 // Field Example1$Wrapper$.MODULE$:LExample1$Wrapper$;
3: iload_1
4: bipush 42
6: invokevirtual #23 // Method Example1$Wrapper$.add$extension:(II)I
9: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LExample1$;
0 10 1 w I
LineNumberTable:
line 11: 3
[...]
С implicit
:
object Example2 {
implicit class Wrapper(val self: Int) extends AnyVal {
def add(n: Int): Int = self + n
}
def foo(w: Wrapper): Wrapper = w.add(42)
}
(Соответствующая часть) байт-код:
scala>:javap Example2
[...]
public int Wrapper(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=1, locals=2, args_size=2
0: iload_1
1: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this LExample2$;
0 2 1 self I
LineNumberTable:
line 9: 0
public int foo(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
0: aload_0
1: getstatic #23 // Field Example2$Wrapper$.MODULE$:LExample2$Wrapper$;
4: iload_1
5: bipush 42
7: invokevirtual #27 // Method Example2$Wrapper$.add$extension:(II)I
10: invokevirtual #29 // Method Wrapper:(I)I
13: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 this LExample2$;
0 14 1 w I
LineNumberTable:
line 12: 0
[...]
В результате расширения AnyVal
призыв к add
вызывается на объекте-компаньоне, а тип Wrapper
не отображается в подписи типа foo
(public int foo(int);
) в обеих версиях.
Hovewer, во второй версии, перед возвратом происходит вызов: 10: invokevirtual #29
, Это вызывает public int Wrapper(int);
который, похоже, ничего не делает. (Хотя я могу ошибаться, так как у меня нет большого опыта чтения байт-кода)
Итак, вопрос в том, каково значение этого призыва? Разве это не может быть опущено?
1 ответ
Проблема в том, что ваши фрагменты кода не эквивалентны. Неявный класс Foo
компилируется / десагерируется в класс Foo
и метод неявного преобразования Foo
, Это также причина того, что неявные классы (в настоящее время) не могут быть верхнего уровня.
Итак, ваш первый фрагмент должен быть:
object Example1 {
class Wrapper(val self: Int) extends AnyVal {
def add(n: Int): Int = self + n
}
def Wrapper(self: Int): Wrapper = new Wrapper(self)
def foo(w: Wrapper): Wrapper = Wrapper(w.add(42))
}
Компилятор стирает вызовы конструкторов классов значений, если это возможно. Но это не стирает призыв к Wrapper
метод, неявный или нет.
Я предполагаю, что JIT-компилятор в JVM в конечном итоге удалит вызов этого метода.