Без всяких лишних слов напишу код:
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".
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Мне нужно пропарсить веб страницу в java, на которой необходимо сперва залогиниться ввести пароль и тдЧитал в интернете , это делают через htmlunit
Есть строка с реквизитами вида AAA,BBBB,AAAAA И есть строка с реквизитами вида AAA, BBBB, AAAAA Так же бывают AAA,BBBB, AAAAA или AAA ,BBBB, AAAAA
Всем привет, в java я новичок, есть приложение на Spring Boot, поставлено на контейнере DockerПри запуске Томкат занимает 8080 порт, и Докер тоже висит...