Страницы

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

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

пятница, 28 февраля 2020 г.

Dart: const и final в чем разница?

#const #dart #final


В чем разница и сходство в Dart'е const и final

Объясните для "чайника" пожалуйста
    


Ответы

Ответ 1



При использовании final - значение может быть присвоено один раз, но любое. При использовании const - накладываются ограничения на присваиваемое значение, оно должно быть доступно в момент компиляции. Так же const уже является final, однако в отличие от final значение не может быть изменено никаким образом. На пример: final a = [1,2,3]; a.add(112); print (a); // [1, 2, 3, 112] const b = [1,2,3]; b.add(111); // Uncaught exception: Unsupported operation: add print (b);

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

Константа static + final, или только final?

#java #static #final


Чтобы создать константу в Java, нужно пометить переменную сразу двумя модификаторами:
static и final. Прочитал это в книге, а если просто переменную final помечаю, тогда
у меня что не константа получается?
    


Ответы

Ответ 1



final достаточно для создания константы. static используется для того, чтобы хранить константу в памяти один раз, а не столько раз, сколько создано экземпляров класса (см. выши предыдущие вопросы).

Ответ 2



Ключевое слово - final заставляет вас проинициализировать переменную сразу же, или в конструкторе, так же для переменных примитивного типа это означает, что однажды присвоенное значение не может быть изменено, и да, ключевое слово final определяет константу, ивозможно у Вас появится вопрос как final работает не с примитивами, так вот, final запрещает переопределять метод, и наследоваться от класса. При использовании final на ссылочном типе данных константой является ссылка, а не сам объект. Неизменяемость или Immutable объекта достигается за счет удаление методов модификации данных.

среда, 25 декабря 2019 г.

Использование final аргумента в локальном классе

#java #final


Без всяких лишних слов напишу код: 

public class A {

    static Object f() {
        String str = "hello";
        str = "world";     //   (1) ошибка компиляции!

        class X extends Object {
            public String toString() {
                return str;
            }
        }

        return new X();
    }

    static Object g() {
        String[] s = { "hello", "silence" };
        s[0] = "World";      //   (2) НЕТ ошибки компиляции.

        class X extends Object{
            public String toString() {
                return s[0];
            }
        }

        return new X();
    }

    public static void main(String[] args) {
    }

}


Закомментирование строки (1) даст компилятору понять, что str в f() есть effectively
final, код скомпилируется.
Закомментирование строки (2) не нужно. Ведь s -- ссылка на массив, которая не изменяется.
Взятие s[0] затем законно.  

Вопрос: Зачем от нас требую использование только final или effectively final локальных
переменных в локальном классе? Я могу работать с первой ячейкой массива s и всё у меня
будет замечательно.



Мои мысли и непонятки: как вообще будет выглядеть код объекта класса f().X ? Это
будет объект с кодом из Object, но его метод toString() будет таким (псевдокод):

public String toString() {
    return 0x47e9c2dd;
}


Где, как понимаю, 0x47e9c2dd адрес той самой строки str? Но если бы оно работало
так, то какая разница какой адрес мы бы положили в этот метод, когда создавали экземпляр
класса f().X ... 



Так же не понимаю: мы определяем метод toString() класса f().X ссылаясь на локальную
переменную str, которая (как ссылка) исчезнет по завершению работы f(), но при том
объект возвращённый из f() содержит в своём toString() ссылку на str (как объект).
Значит у нас тратится RAM на то, чтобы держать метод f().X.toString() определённым?....



В общем я очень запутался, совершенно не понимаю происходящего с этими локальными
классами в том свете, что на нас возложено это странное требование на final или effectively
final. Объясните происходящее, пожалуйста...
    


Ответы

Ответ 1



В Java замыкания захватывают значения, а не переменные. При компиляции внутреннего класса, Java создаст в нём поле для хранения значения переменной, "захваченной" из внешней области видимости: $ javac A.java $ javap A$1X Compiled from "A.java" class A$1X { final java.lang.String val$str; // <-- "hello" A$1X(); public java.lang.String toString(); } У требования к неизменяемости обеих переменных есть несколько причин. Уверен, самым главным было то, что Java позиционируется как язык, в корне пресекающий возможность совершения множества ошибок и вынуждающий программистов писать правильный код. А классические замыкания - это не самая простая и интуитивно понятная тема. Поэтому в Java замыкания сознательно ограничили и сделали более простыми. Кроме того, если реализовывать классические замыкания, то необходимо было бы каким-то образом сохранять захваченную переменную. Например, выделять место в куче. Во-первых, это потребовало бы доработки виртуальной машины и, возможно, изменения байт-кода. Во-вторых, это повод для утечек памяти. Можно было бы сделать поле val$str изменяемым, но это усложнило бы и замедлило использование анонимных классов и лямбда-выражений в многопоточной среде. Можно было бы дать программисту возможность выбирать, когда это поле финальное, а когда нет, но тогда оно перестаёт быть неявным и мы опять приходит к необходимости доработки виртуальной машины, усложнению языка и поводам для ошибок. Можно было бы не ограничивать изменяемость переменной из внешней области видимости, но на уровне языка переменная str одна, вероятность того, что в разных областях видимости она может иметь разные значения - это ещё больший повод для ошибок, чем классические замыкания. Подытоживая, ограничение на неизменяемость переменных одновременно сделало и язык лучше, и техническую реализацию замыканий проще. Кое-что об этом можно почитать у Брайана Гетца в "State of the Lambda: Variable capture".

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

Сравнение строк c модификатором final

#java #строки #final


Подскажите пож-ста, почему модификатор final меняет результат сравнения?

String str4 = "socialmedia";

String str1 = "social";
String str2 = "media";
String str3 = str1 + str2;
System.out.println(str3 == str4); // false

final String str11 = "social";
final String str21 = "media";
String str31 = str11 + str21;
System.out.println(str31 == str4); // true

    


Ответы

Ответ 1



При создании экземпляра класса String путем присваивания его ссылки на литерал, последний помещается в так называемый «пул литералов». Если в дальнейшем будет создана еще одна ссылка на литерал, эквивалентный ранее объявленному, то будет произведена попытка добавления его в «пул литералов». Так как идентичный литерал там уже существует, то дубликат не может быть размещен, и вторая ссылка будет на существующий литерал. Аналогично в случае, если литерал является вычисляемым. То есть компилятор воспринимает литералы "socialmedia" и "social" + "media" как эквивалентные. В данном случае слияние финализированных строк в String str31 = str11 + str21; это то же самое что и String str31 = "social" + "media". В итоге str31 будет ссылатся на ту же самую область «пула литералов» что и String str4 = "socialmedia"; А т.к. String str3 = str1 + str2; (слияние нефинализированных ссылок) то эта переменная уже не попадет в «пул литералов» и сравнение ссылок приведет к false.

среда, 28 ноября 2018 г.

Использование final аргумента в локальном классе

Без всяких лишних слов напишу код:
public class A {
static Object f() { String str = "hello"; str = "world"; // (1) ошибка компиляции!
class X extends Object { public String toString() { return str; } }
return new X(); }
static Object g() { String[] s = { "hello", "silence" }; s[0] = "World"; // (2) НЕТ ошибки компиляции.
class X extends Object{ public String toString() { return s[0]; } }
return new X(); }
public static void main(String[] args) { }
}
Закомментирование строки (1) даст компилятору понять, что str в f() есть effectively final, код скомпилируется. Закомментирование строки (2) не нужно. Ведь s -- ссылка на массив, которая не изменяется. Взятие s[0] затем законно.
Вопрос: Зачем от нас требую использование только final или effectively final локальных переменных в локальном классе? Я могу работать с первой ячейкой массива s и всё у меня будет замечательно.

Мои мысли и непонятки: как вообще будет выглядеть код объекта класса f().X ? Это будет объект с кодом из Object, но его метод toString() будет таким (псевдокод):
public String toString() { return 0x47e9c2dd; }
Где, как понимаю, 0x47e9c2dd адрес той самой строки str? Но если бы оно работало так, то какая разница какой адрес мы бы положили в этот метод, когда создавали экземпляр класса f().X ...

Так же не понимаю: мы определяем метод toString() класса f().X ссылаясь на локальную переменную str, которая (как ссылка) исчезнет по завершению работы f(), но при том объект возвращённый из f() содержит в своём toString() ссылку на str (как объект). Значит у нас тратится RAM на то, чтобы держать метод f().X.toString() определённым?....

В общем я очень запутался, совершенно не понимаю происходящего с этими локальными классами в том свете, что на нас возложено это странное требование на final или effectively final. Объясните происходящее, пожалуйста...


Ответ

В Java замыкания захватывают значения, а не переменные. При компиляции внутреннего класса, Java создаст в нём поле для хранения значения переменной, "захваченной" из внешней области видимости:
$ javac A.java $ javap A$1X
Compiled from "A.java" class A$1X { final java.lang.String val$str; // <-- "hello" A$1X(); public java.lang.String toString(); }
У требования к неизменяемости обеих переменных есть несколько причин.
Уверен, самым главным было то, что Java позиционируется как язык, в корне пресекающий возможность совершения множества ошибок и вынуждающий программистов писать правильный код. А классические замыкания - это не самая простая и интуитивно понятная тема. Поэтому в Java замыкания сознательно ограничили и сделали более простыми.
Кроме того, если реализовывать классические замыкания, то необходимо было бы каким-то образом сохранять захваченную переменную. Например, выделять место в куче. Во-первых, это потребовало бы доработки виртуальной машины и, возможно, изменения байт-кода. Во-вторых, это повод для утечек памяти.
Можно было бы сделать поле val$str изменяемым, но это усложнило бы и замедлило использование анонимных классов и лямбда-выражений в многопоточной среде. Можно было бы дать программисту возможность выбирать, когда это поле финальное, а когда нет, но тогда оно перестаёт быть неявным и мы опять приходит к необходимости доработки виртуальной машины, усложнению языка и поводам для ошибок.
Можно было бы не ограничивать изменяемость переменной из внешней области видимости, но на уровне языка переменная str одна, вероятность того, что в разных областях видимости она может иметь разные значения - это ещё больший повод для ошибок, чем классические замыкания.
Подытоживая, ограничение на неизменяемость переменных одновременно сделало и язык лучше, и техническую реализацию замыканий проще.
Кое-что об этом можно почитать у Брайана Гетца в "State of the Lambda: Variable capture".