Страницы

Поиск по вопросам

Показаны сообщения с ярлыком константа. Показать все сообщения
Показаны сообщения с ярлыком константа. Показать все сообщения

четверг, 27 февраля 2020 г.

Что в Java является константой, а что - нет?

#java #переменные #интерфейс #константа


До недавнего времени я был почти на 100% убеждён в том, что прекрасно понимаю сущность
констант в языке программирования Java. Так было, пока я не наткнулся на один из примеров,
который был представлен в JLS (Java Language Specification). Он немного поверг меня
в ступор, и я не могу разобраться с этим вопросом вплоть до текущего момента.

Наверное, я не сильно ошибусь, если скажу, что типичных констант в языке программирования
Java нет вообще (во всяком случае, если смотреть с колокольни какого-нибудь С++). Даже
ключевое слово const, которое зарезервировано с незапамятных времён, но до сих пор
не используется, как бы ненавязчиво намекает нам об этом (не уверен, но вроде бы это
было сделано для того, чтобы сохранить обратную совместимость с С++ в том случае, если
константы всё-таки будут введены в язык).

В качестве некоторого общепринятого суррогата используется связка модификаторов static
final в процессе декларирования переменной. Если я правильно понимаю, то именно такие
переменные и считаются константами с точки зрения программистского сообщества ЯП Java.
В то же время, если заглянуть на просторах JLS в параграф, который посвящён инициализации
классов и интерфейсов, мы можем наткнуться на один интересный момент. Небольшая выдержка
оттуда:


  Example 12.4.1-3. Interface Initialization Does Not Initialize Superinterfaces


interface I {
    int i = 1, ii = Test.out("ii", 2);
}
interface J extends I {
    int j = Test.out("j", 3), jj = Test.out("jj", 4);
}
interface K extends J {
    int k = Test.out("k", 5);
}
class Test {
    public static void main(String[] args) {
        System.out.println(J.i);
        System.out.println(K.j);
    }
    static int out(String s, int i) {
        System.out.println(s + "=" + i);
        return i;
    }
}



  This program produces the output:
  
  1
  j=3
  jj=4
  3 
  
  The reference to J.i is to a field that is a constant
  variable (§4.12.4); therefore, it does not cause I to be initialized
  (§13.4.9).
  
  The reference to K.j is a reference to a field actually declared in
  interface J that is not a constant variable; this causes
  initialization of the fields of interface J, but not those of its
  superinterface I, nor those of interface K.
  
  Despite the fact that the name K is used to refer to field j of
  interface J, interface K is not initialized.


И вот этот момент мне совершенно непонятен! В интерфейсах все поля неявным образом
помечаются как public static final (другими они и не могут быть), что мы и видим в
приведённом примере. Все поля имеют совершенно одинаковый набор модификаторов, но при
этом, поле J.i называют константной переменной, а K.j константой уже не является, если
верить примеру, который приведён в JLS.

Что же такое константа с точки зрения JLS? Неужели это не абсолютно все статические
финализированные поля? Правильно ли я понял, что при присваивании значения через его
возврат функцией, мы уже не можем считать поле константой?
    


Ответы

Ответ 1



С точки зрения JLS: A constant variable is a final variable of primitive type or type String that is initialized with a constant expression То есть константа - это final переменная (причем речь не только о static и не только о полях) примитивного типа или типа String, которая проинициализирована константным выражением Вызов метода константным выражением не является, поэтому K.j - это не константа

воскресенье, 9 февраля 2020 г.

Пул констант в Java

#java #переменные #const #константа #pool


Не раз слышал о так называемом пуле констант в языке программирования Java. Знаю
о пуле объектов типа String, пуле для типов Byte, Short, Character, Integer, Long и
даже Boolean. Также знаю, что мы сами можем определять размер пула типа Integer, если
запускать приложение с параметром -Djava.lang.Integer.IntegerCache.high=XX, где XX
может колебаться в диапазоне от 127 до (Integer.MAX_VALUE - 129). Весь этот пул представляет
собой массив, каждое значение которого является элементом расположенным в порядке числового
возрастания и мы можем напрямую обратиться к нему по индексу за константное время O(1).
Я прекрасно понимаю, где хранится этот массив. У каждого целочисленного обёрточного
типа есть свой вложенный класс такого типа: 

private static class WrappingСlassNameCache {...}  


Где вместо WrappingСlassNameCache, мы подставляем конкретное имя класса, к примеру,
IntegerCache. 
Внутри это выглядит так: 

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }


Ну Бог с ним, тут ничего мудрёного нет! А что же такое пул констант? Что он из себя
представляет? И вообще где хранятся все константы в Java? Если мы говорим о локальных
финализированных переменных, то они хранятся в стековой памяти. Если мы имеем дело
со static final полями, то эти поля хранятся в MetaSpace (раньше в PermGen), если мы
говорим о реализации JVM HotSpot. Так о каком вообще пуле может идти речь? Как всё
это реализовано? Описано в каком-то классе, который поставляется вместе со стандартным
API от Oracle (раньше Sun Microsystems) или же это нужно заглядывать в реализацию конкретной
JVM и читать JVMS? Подскажите, пожалуйста, надоело, что в которой раз сталкиваюсь с
этим понятием и никак не могу понять о чём идёт речь. Всем огромное спасибо за помощь! :)
    


Ответы

Ответ 1



У каждого класса свой пул констант. Чтобы понять для чего он нужен, разберём простой пример class Example { public void hello() { System.out.println("Hello"); } } Скомпилируем $ javac Example.java И заглянем внутрь $ javap -c -v Example.class public class Example minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#14 // java/lang/Object."":()V #2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #17 // Hello #4 = Methodref #18.#19 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #20 // Example #6 = Class #21 // java/lang/Object #7 = Utf8 #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 hello #12 = Utf8 SourceFile #13 = Utf8 Example.java #14 = NameAndType #7:#8 // "":()V #15 = Class #22 // java/lang/System #16 = NameAndType #23:#24 // out:Ljava/io/PrintStream; #17 = Utf8 Hello #18 = Class #25 // java/io/PrintStream #19 = NameAndType #26:#27 // println:(Ljava/lang/String;)V #20 = Utf8 Example #21 = Utf8 java/lang/Object #22 = Utf8 java/lang/System #23 = Utf8 out #24 = Utf8 Ljava/io/PrintStream; #25 = Utf8 java/io/PrintStream #26 = Utf8 println #27 = Utf8 (Ljava/lang/String;)V { public Example(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return LineNumberTable: line 1: 0 public void hello(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 3: 0 line 4: 8 } Первое, что представляет интерес - это байткоды метода hello: 0: getstatic #2 3: ldc #3 5: invokevirtual #4 Цифры до двоеточия обозначают байт, с которого начинается байткод и его параметры. По шагу видно, что каждая операция - это два байта, один байт на код операции и один байт на параметр. Байткоды могут принимать только параметры фиксированного размера - integer, long, short, byte, character, float, double, reference. Чтобы передать в метод println строку "Hello", надо загрузить в стек ссылку на строку "Hello", а саму строку где-то сохранить. Вот это где-то как раз и есть пул констант. А символы #3 после байткода ldc - ссылка на 3-й элемент в пуле констант. Constant pool: #1 = Methodref #6.#14 // java/lang/Object."":()V #2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #17 // Hello #4 = Methodref #18.#19 // java/io/PrintStream.println:(Ljava/lang/String;)V Как можно увидеть из этого кусочка пула, его элементы сами часто ссылаются в пул. В частности наша строка под номером 3 ссылается на массив символов под номером 17. Если говорить о расположении пула констант в памяти JVM, то это один из участков MetaSpace.

среда, 1 января 2020 г.

Для чего делать финализированную переменную статической?

#java #переменные #константа #статические_члены #финализированные_переменные


Для чего делать финализированную переменную статической?
В смысле, в каких случаях это имеет смысл и почему нельзя обойтись без этого?
    


Ответы

Ответ 1



Если коротко - можно обойтись и без этого, но так оптимальнее по памяти в общем случае. static final принадлежит классу, соответственно не зависимо от количества экземпляров этого класса будет создан только один объект типа static final переменной. В случае с просто final каждый объект будет содержать ссылку на новую копию. Если такая переменная содержит ссылку на объект, а не на примитивный тип, значение которого может быть закешировано (intern) - память будет израсходована на хранение одинаковых объектов. —- Так же есть различия в жизненном цикле(порядок инициализации). Статика будет проинициализирована во время загрузки класса класс-лоадером. Т.е. даже если ни один экземпляр этого класса никогда не будет создан. Просто final же напротив - будет проинициализирована в момент создания экземпляра. UPD: Таким образом получается что для всяких там строк и других immutable объектов, можно ставить static, чтобы не плодить идентичные копии, однако для объектов, которые содержат в себе какое-то состояние, которое может быть изменено со временем - уже стоит подумать ставить static или нет. Т.е. наличие директивы final еще не говорит о том что объект не поменяется со временем. final это атрибут поля, но никак не объекта на который ссылается это поле.

воскресенье, 8 декабря 2019 г.

Как запретить изменение переменной?

#java #классы #переменные #доступ #константа


Как сделать, чтобы переменную можно было изменять только из текущего класса и при
обращении из других классов она была, грубо говоря, как "константа", то есть с доступом
только на чтение?
    


Ответы

Ответ 1



Не припомню таких спецификаторов в языке Java. Но можно сделать, например, private поле с public getter() и НЕ public setter().

Ответ 2



Если нужен доступ в режиме readonly, то можно написать примерно так: public class Foo { private int bar; public int getBar() { return bar; } }

суббота, 7 декабря 2019 г.

Локальная константа времени выполнения

#c_sharp #константа #дизайн_языка


В C++ для определения локальной константы времени выполнения можно написать так:

const auto c = f();


Далее, все попытки изменить c будут приводить к ошибке компиляции.

В C# такой возможности нет. Можно использовать readonly член подобным образом, но
не локальную константу. 

Почему существует такое ограничение (в чём причина отсутствия локальных констант
а-ля C++) и какое каноническое решение имеется для обеспечения локальной константности
времени выполнения на C#? Неужели для этого надо создавать отдельный read-only интерфейс?
    


Ответы

Ответ 1



Стоит отметить, что по локальным readonly-переменным есть официальный proposal. Фича опоздала к C# 7, но у нее все шансы войти в C# 8. Со стороны CLR для реализации нет никаких ограничений - readonly locals, как и все фичи C# со времен .NET 4.0 - это compile-time фичи - т.к. версия рантайма с тех пор не поменялась. По сути, proposal сводится к нескольким пунктам: возможности помечать параметры как readonly возможности помечать переменные как readonly шорткату let - эквиваленту readonly var Локальные readonly, как и неизменяемые foreach iteration variable - это, прежде всего, защита от дурака/ошибки копипасты. И уже потом - способ указать компилятору на возможную оптимизацию замыканий. На мой взгляд, сама по себе возможность вручную приписывать readonly к локальным переменным только ради в качестве защиты от потенциальной ошибки - неудобна для реального использования. Достаточно вспомнить о final в Java. Поэтому без шортката let фича будет достаточно бесполезной - ее будут использовать фанаты "безопасного кода" - те, кто сейчас принципиально не использует var и явно вписывают типы во всех упоминаниях генерика. Остальные пойдут по пути наименьшего сопротивления - гораздо дешевле и проще исправить одну ошибку раз в месяц, чем приписывать readonly к каждой переменной. Ну и опять же, тесты, при правильном применении, решают проблему ошибок копипасты. Более надежный способ защиты реализован, например, в F#, где локальные значения по умолчанию неизменяемы, а = вне объявления значения вообще не работает как оператор присвоения. let x = 1 x = 2 // This expression should have type 'unit', but has type 'bool' Для изменения значения разработчику придется сделать дополнительные телодвижения, как в строчке где переменная объявлена, так и в строчке, где переменная изменяется. let mutable x = 1 x <- 2 Сам язык подталкивает его к тому, чтобы объявить еще одно значение, а не менять существующее. Одна из трех добродетелей программиста - лень - уберегает его от ошибки. А просто так дописывать readonly к переменным, "как бы чего не вышло" - никто, кроме особых фанатов, не будет - из соображений той же лени.

Ответ 2



Ответы: JaredPar, и Jon Skeet Одна из причин - отсутствие поддержки CLR для локальных переменных только для чтения. Readonly переводится в CLR/CLI initonly опкод. Этот флаг может быть применен только к полям и не имеет смысла для локальных переменных. Фактически, применение этого кода к локальным переменным скорее всего сделает код непроверяемым. Это не значит, что C# не может реализовать это. Но это добавляет два смысла для одной и той же языковой конструкции. Версия для локальных переменных не будет иметь в CLR эквивалентного отображения. Обращаясь к ответу Jared'а, это возможно может быть compile-time фичей - компилятор будет запрещать тебе писать в переменную после первоначального объявления (которое должно включать присвоение). Есть ли в этом польза? Потенциальная - но не большая, если быть честным. Если ты не можешь сказать будет или нет присваивание еще где-то в методе, то твой метод слишком большой. Как бы то ни было, в Java есть эта особенность (при использовании модификатора final) и я очень редко видел его использование в случаях отличных от разрешения использования переменной в внутреннем анонимном классе - и где это было использовано это давало скорее впечатление беспорядка, чем полезной информации. в качестве обходного пути, если очень хочется, только на свой страх и риск есть такой: Настолько отличный, насколько ужасный пример В качестве run-time константы можно использовать переменную в блоке итератора: public void f() { foreach (int n in new int[] { getNum() }) // n declared constant { n = 3; // won't compile: "error CS1656: Cannot assign to 'n' because it is a 'foreach iteration variable'" } }

среда, 27 ноября 2019 г.

Почему const и goto не используются в java?

#java #синтаксис #константа #goto


Давайте поговорим о двух зарезервированных, но неиспользуемых ключевых словах языка
Java — const и goto. Почему они не используются? Как я понял const можно заменить на
public static final, но как быть с goto?
    


Ответы

Ответ 1



Обновление В спецификации Java (§3.9. Keywords) указана причина резервирования const и goto, не связанная с планами на будущее: The keywords const and goto are reserved, even though they are not currently used. This may allow a Java compiler to produce better error messages if these C++ keywords incorrectly appear in programs. Ключевые слова const и goto зарезервированы, хотя на данный момент не используются. Это позволит компилятору Java выводить более ясные сообщения об ошибках если эти ключевые слова C++ будут неправильно использоваться в программах. Т.о. разработчики Java изначально не имели планов использовать эти слова, а зарезервировали их чтобы отловить ошибки при копировании кода, написанного на C++ (если бы переменные можно было бы назвать goto либо const, то скопированный код C++ мог ошибочно пройти компиляцию и привести к неожиданным результатам при исполнении). Эта часть спецификации не изменялась с первого издания JLS. Ниже первоначальная версия ответа, в которой рассматриваются попытки найти применение для const и goto в Java. Многа букав: const и goto перешли из C++. Ключевым словам не нашли применения в Java. Функции, которые они выполняют в C++, в Java реализованы по-другому. История Исторически синтаксис Java основан на C++, поэтому разработчики приняли решение зарезервировать ряд ключевых слов C++, чтобы: отловить ошибки при копировании кода, написанного на C++; использовать их в дальнейшем при расширении языка; упростить переход на Java для C++-разработчиков. const и goto Таким образом вопрос сводиться к следующему: «Есть ли в Java необходимость в const и goto и что они должны делать?» const Одно из предложений ввести const по аналогии с C++ можно посмотреть по ссылке: JDK-4211070 : Java should support const parameters (like C++) for code maintainence Предложение было закрыто в 2005 году. По ссылке можно посмотреть разбор, в выводах которого отмечаются проблемы, связанные вводом const: Adding const is too late now. Had this been added from 1.0, the situation could have been different. Const pollution: the C++ approach requires all const methods to be marked with a const keyword. This means that most methods will have to be marked const explicitly. This tend to clutter all methods in C++. Compatibility is a very important feature of the JDK. Arguably, the collection classes should be modified to indicate that the elements are const. That would require all existing implementations to be updated in the same way, effectively breaking all existing non-JDK implementations of the collection interfaces. Similarly, hashCode would have to be const, breaking the current implementation of String. Создание константных переменных/полей в Java уже обеспечивается с помощью final. Ввод механизмов из C++ посчитали неоправданным. goto У goto плохая репутация (см. «Go To Statement Considered Harmful» Дейкстры). В Java имеются альтернативные операторы перехода: break, continue, return, которые покрывают большую часть области применения. Единственный довод в пользу goto состоит в том, что в отдельных случаях использование goto позволяет упростить код. По-видимому, сторонники goto не смогли оказать достаточного влияния на разработчиков, чтобы добавить новое ключевое слово. Похожие обсуждения в англиской версии: Why is there no Constant feature in Java? Is there a goto statement in Java?

Ответ 2



goto считается "плохим оператором", ухудшает читаемость и увеличивает запутанность кода, порождает трудноподдерживаемый "спагетти-код" (также нарушает очень важные концепции программирования, можно ознакомиться по ссылке в конце ответа). Некоторые серьезные люди считают, что ему нет места в языках программирования, и пишут об этом целые книги. Так вот, пока кто-то говорит об этом, создатели Java - делают. Они искренне считают, что без безусловного перехода мир станет чуточку добрее, и по совокупности проблем и преимуществ с ними можно согласиться. Подробнее Само же слово было зарезервировано на этапе создания языка, перекочевав из других популярных в то время. Так и болтается до сих пор... PS: в Java все же есть безусловные переходы, реализуемые через операторы break и continue с метками. Как часто вы ими пользовались и знали ли об этой возможности вообще (это по поводу нужности этого оператора)?

Ответ 3



Например они могут не использовать, но быть в языке, как задел на будущее, что бы в последующих версиях можно было легко добавить этот оператор и не пересечься с названием переменных/объектов в уже написанном(Legacy) на старой версии языка коде. В JavaScript, вроде, такая же практика. Касательно const, который можно заменить public static final: Ничего не мншает разработчикам JAVA в будущем реализовать const и добавить так называемых "синтаксический сахар", когда можно делать и так и так, и в итоге программа будет одинаково выполняться. Например, в C# много синтаксического сахара: Можно объявлять переменные по алиасу(int) или по самому типу(Int32) Есть LINQ, который позволяет в простых случаях обходится без циклов и т д...

понедельник, 15 апреля 2019 г.

Пул констант в Java

Не раз слышал о так называемом пуле констант в языке программирования Java. Знаю о пуле объектов типа String, пуле для типов Byte, Short, Character, Integer, Long и даже Boolean. Также знаю, что мы сами можем определять размер пула типа Integer, если запускать приложение с параметром -Djava.lang.Integer.IntegerCache.high=XX, где XX может колебаться в диапазоне от 127 до (Integer.MAX_VALUE - 129). Весь этот пул представляет собой массив, каждое значение которого является элементом расположенным в порядке числового возрастания и мы можем напрямую обратиться к нему по индексу за константное время O(1). Я прекрасно понимаю, где хранится этот массив. У каждого целочисленного обёрточного типа есть свой вложенный класс такого типа:
private static class WrappingСlassNameCache {...}
Где вместо WrappingСlassNameCache, мы подставляем конкретное имя класса, к примеру, IntegerCache. Внутри это выглядит так:
private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[];
static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h;
cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; }
Ну Бог с ним, тут ничего мудрёного нет! А что же такое пул констант? Что он из себя представляет? И вообще где хранятся все константы в Java? Если мы говорим о локальных финализированных переменных, то они хранятся в стековой памяти. Если мы имеем дело со static final полями, то эти поля хранятся в MetaSpace (раньше в PermGen), если мы говорим о реализации JVM HotSpot. Так о каком вообще пуле может идти речь? Как всё это реализовано? Описано в каком-то классе, который поставляется вместе со стандартным API от Oracle (раньше Sun Microsystems) или же это нужно заглядывать в реализацию конкретной JVM и читать JVMS? Подскажите, пожалуйста, надоело, что в которой раз сталкиваюсь с этим понятием и никак не могу понять о чём идёт речь. Всем огромное спасибо за помощь! :)


Ответ

У каждого класса свой пул констант. Чтобы понять для чего он нужен, разберём простой пример
class Example { public void hello() { System.out.println("Hello"); } }
Скомпилируем
$ javac Example.java
И заглянем внутрь
$ javap -c -v Example.class
public class Example minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#14 // java/lang/Object."":()V #2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #17 // Hello #4 = Methodref #18.#19 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #20 // Example #6 = Class #21 // java/lang/Object #7 = Utf8 #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 hello #12 = Utf8 SourceFile #13 = Utf8 Example.java #14 = NameAndType #7:#8 // "":()V #15 = Class #22 // java/lang/System #16 = NameAndType #23:#24 // out:Ljava/io/PrintStream; #17 = Utf8 Hello #18 = Class #25 // java/io/PrintStream #19 = NameAndType #26:#27 // println:(Ljava/lang/String;)V #20 = Utf8 Example #21 = Utf8 java/lang/Object #22 = Utf8 java/lang/System #23 = Utf8 out #24 = Utf8 Ljava/io/PrintStream; #25 = Utf8 java/io/PrintStream #26 = Utf8 println #27 = Utf8 (Ljava/lang/String;)V { public Example(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return LineNumberTable: line 1: 0
public void hello(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 3: 0 line 4: 8 }
Первое, что представляет интерес - это байткоды метода hello
0: getstatic #2 3: ldc #3 5: invokevirtual #4
Цифры до двоеточия обозначают байт, с которого начинается байткод и его параметры. По шагу видно, что каждая операция - это два байта, один байт на код операции и один байт на параметр. Байткоды могут принимать только параметры фиксированного размера - integer, long, short, byte, character, float, double, reference. Чтобы передать в метод println строку "Hello", надо загрузить в стек ссылку на строку "Hello", а саму строку где-то сохранить. Вот это где-то как раз и есть пул констант. А символы #3 после байткода ldc - ссылка на 3-й элемент в пуле констант.
Constant pool: #1 = Methodref #6.#14 // java/lang/Object."":()V #2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #17 // Hello #4 = Methodref #18.#19 // java/io/PrintStream.println:(Ljava/lang/String;)V
Как можно увидеть из этого кусочка пула, его элементы сами часто ссылаются в пул. В частности наша строка под номером 3 ссылается на массив символов под номером 17.
Если говорить о расположении пула констант в памяти JVM, то это один из участков MetaSpace.

среда, 10 октября 2018 г.

Локальная константа времени выполнения

В C++ для определения локальной константы времени выполнения можно написать так:
const auto c = f();
Далее, все попытки изменить c будут приводить к ошибке компиляции.
В C# такой возможности нет. Можно использовать readonly член подобным образом, но не локальную константу.
Почему существует такое ограничение (в чём причина отсутствия локальных констант а-ля C++) и какое каноническое решение имеется для обеспечения локальной константности времени выполнения на C#? Неужели для этого надо создавать отдельный read-only интерфейс?


Ответ

Стоит отметить, что по локальным readonly-переменным есть официальный proposal. Фича опоздала к C# 7, но у нее все шансы войти в C# 8.
Со стороны CLR для реализации нет никаких ограничений - readonly locals, как и все фичи C# со времен .NET 4.0 - это compile-time фичи - т.к. версия рантайма с тех пор не поменялась.
По сути, proposal сводится к нескольким пунктам:
возможности помечать параметры как readonly возможности помечать переменные как readonly шорткату let - эквиваленту readonly var

Локальные readonly, как и неизменяемые foreach iteration variable - это, прежде всего, защита от дурака/ошибки копипасты. И уже потом - способ указать компилятору на возможную оптимизацию замыканий.
На мой взгляд, сама по себе возможность вручную приписывать readonly к локальным переменным только ради в качестве защиты от потенциальной ошибки - неудобна для реального использования. Достаточно вспомнить о final в Java. Поэтому без шортката let фича будет достаточно бесполезной - ее будут использовать фанаты "безопасного кода" - те, кто сейчас принципиально не использует var и явно вписывают типы во всех упоминаниях генерика.
Остальные пойдут по пути наименьшего сопротивления - гораздо дешевле и проще исправить одну ошибку раз в месяц, чем приписывать readonly к каждой переменной. Ну и опять же, тесты, при правильном применении, решают проблему ошибок копипасты.
Более надежный способ защиты реализован, например, в F#, где локальные значения по умолчанию неизменяемы, а = вне объявления значения вообще не работает как оператор присвоения.
let x = 1 x = 2 // This expression should have type 'unit', but has type 'bool'
Для изменения значения разработчику придется сделать дополнительные телодвижения, как в строчке где переменная объявлена, так и в строчке, где переменная изменяется.
let mutable x = 1 x <- 2
Сам язык подталкивает его к тому, чтобы объявить еще одно значение, а не менять существующее. Одна из трех добродетелей программиста - лень - уберегает его от ошибки.
А просто так дописывать readonly к переменным, "как бы чего не вышло" - никто, кроме особых фанатов, не будет - из соображений той же лени.