#java #scala #jvm
#java #scala #jvm
Вопрос:
Когда мы создаем финал в java, гарантируется, что он не может быть изменен даже во время выполнения, потому что JVM гарантирует это.
Класс Java:
public class JustATest {
public final int x = 10;
}
Javap декомпилирован:
Скомпилировано из «JustATest.java «
public class JustATest {
public final int x;
public JustATest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 10
7: putfield #2 // Field x:I
10: return
}
Но в scala, если мы объявляем a val
, он компилируется в обычное целое число, и нет никакой разницы между var и val с точки зрения вывода декомпиляции.
Оригинальный класс Scala:
class AnTest {
val x = 1
var y = 2
}
Декомпилированный вывод:
Compiled from "AnTest.scala"
public class AnTest {
public int x();
Code:
0: aload_0
1: getfield #14 // Field x:I
4: ireturn
public int y();
Code:
0: aload_0
1: getfield #18 // Field y:I
4: ireturn
public void y_$eq(int);
Code:
0: aload_0
1: iload_1
2: putfield #18 // Field y:I
5: return
public AnTest();
Code:
0: aload_0
1: invokespecial #25 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_1
6: putfield #14 // Field x:I
9: aload_0
10: iconst_2
11: putfield #18 // Field y:I
14: return
}
С этой информацией концепция неизменности a val
контролируется компилятором scala только во время компиляции? Как это гарантируется во время выполнения?
Комментарии:
1. Как вы сказали, неизменность значения гарантируется только во время компиляции. И вы действительно можете изменить значение, используя отражение во время выполнения.
2. Единственный способ изменить значение во время выполнения — это если вы выполняете отражение в своем коде. Какие «гарантии времени выполнения» вы ищете?
3. @YuvalItzchakov: Концепция неизменности значения отсутствует в java, т.е. Значение int может изменяться, но в случае scala значение val гарантирует, что оно не может быть изменено. В таком случае, как это сообщается с jvm, поскольку значение val не может вести себя как final
4. Это не отражается в байт-коде JVM, поскольку оно применяется во время компиляции . Если вы пытаетесь изменить
val
, ваш код не будет компилироваться. Зачем вам нужно передавать его в байт-код?5. К вашему сведению
final
, в Java это также просто ограничение времени компиляции. Вы можете изменятьfinal
поля.
Ответ №1:
В Scala передача неизменности через val
— это принудительное выполнение во время компиляции, которое не имеет ничего общего с передаваемым байтовым кодом. В Java вы заявляете, что, когда поле предназначено final
для того, чтобы оно не было переназначено, где в Scala объявление переменной val
только с означает, что она не может быть переназначена, но ее можно переопределить. Если вы хотите, чтобы поле было final
, вам нужно указать его, как вы делаете в Java:
class AnTest {
final val x = 10
}
Что дает:
public class testing.ReadingFile$AnTest$1 {
private final int x;
public final int x();
Code:
0: bipush 10
2: ireturn
public testing.ReadingFile$AnTest$1();
Code:
0: aload_0
1: invokespecial #19 // Method java/lang/Object."<init>":()V
4: return
}
Что эквивалентно байт-коду, который вы видите в Java, за исключением того, что компилятор выдал средство получения x
.
Ответ №2:
Действительно простой ответ: есть некоторые функции Scala, которые могут быть закодированы в байт-коде JVM, а некоторые — нет.
В частности, существуют некоторые ограничения, которые не могут быть закодированы в байт-коде JVM, например sealed
, или private[this]
, или val
. Это означает, что если вы получите в свои руки скомпилированный байт-код JVM исходного файла Scala, то вы сможете делать то, что вы не можете сделать из Scala, взаимодействуя с кодом через язык, который не является Scala.
Это не относится к серверной части JVM, у вас похожие и даже более выраженные проблемы с Scala.js , поскольку цель компиляции здесь (ECMAScript) предлагает еще меньше способов выражения ограничений, чем байт-код JVM.
Но на самом деле это всего лишь общая проблема: я могу взять такой безопасный и чистый язык, как Haskell, скомпилировать его в машинный код, и если я получу в свои руки скомпилированный двоичный файл, вся безопасность будет потеряна. Фактически, большинство компиляторов Haskell выполняют (почти) полное удаление типов, поэтому после компиляции буквально не остается типов и ограничений типов.