class ObjectReference<T> {
T value;
}
var stringRef2 = new ObjectReference<String>();
((Runnable) () -> {
stringRef2.value = "result";
}).run();
System.out.println(stringRef2.value);
Можно использовать какой-нибудь существующий класс вроде AtomicReference, но вообще-то он тут не совсем к месту, мне обычной ссылки за глаза хватит.
Есть ли где-нибудь в JDK уместный в данном случае класс, чтобы не писать свой?
Здравствуйте, GarryIV, Вы писали:
vsb>>Нужно вернуть значение из лямбды. GIV>Ну верни. Зачем Runnable тогда тебе когда есть куча всего https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/util/function/package-summary.html? GIV>И своего можно наделать в добавок. GIV>Из Runnable понятно нельзя. Зачем тебе оно? GIV>В качестве костыля что хочешь то и используй, но лучше таки понять как без костылей жить.
Мне надо несколько вернуть. record-ы на ровном месте городить не хочу, это точно некрасиво.
Здравствуйте, vsb, Вы писали:
GIV>>В качестве костыля что хочешь то и используй, но лучше таки понять как без костылей жить. vsb>Мне надо несколько вернуть.
Так бы и сказал что тебе туплы нужны (если я правильно понял что несколько != List<T>). Тут лямбды вообще не причем. https://www.baeldung.com/java-method-return-multiple-values https://www.baeldung.com/java-tuples
vsb>record-ы на ровном месте городить не хочу, это точно некрасиво.
То есть чистая функция не красиво а вот функция с сайд эффектом на ровном месте красиво?
Ну ну.
Здравствуйте, vsb, Вы писали:
vsb>Есть ли где-нибудь в JDK уместный в данном случае класс, чтобы не писать свой?
Не очень ясно как оно должно работать. Ведь никакой гарантии, что метод лямбды будет вызван, притом ровно один раз и из того же треда. Поэтому всё равно надо как-то инициализировать значение и давать понять, что оно может несколько раз изменяться.
Есть такой трюк с анонимным типом и var:
var thing = new Runnable() {
int r = -1;
@Override
public void run() {
r = 42;
}
};
thing.run();
System.out.println(thing.r);
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, GarryIV, Вы писали:
vsb>>record-ы на ровном месте городить не хочу, это точно некрасиво. GIV>То есть чистая функция не красиво а вот функция с сайд эффектом на ровном месте красиво? GIV>Ну ну.
Если бы в жаве были туплы, которым не надо давать имя, я бы их использовал.
Заводить record некрасиво. Да, я знаю, что его даже можно внутри функции объявить. Всё равно некрасиво. У него не может быть читаемого имени. Ещё и куча писанины.
Здравствуйте, ·, Вы писали:
·>Не очень ясно как оно должно работать. Ведь никакой гарантии, что метод лямбды будет вызван, притом ровно один раз и из того же треда.
Почему нет гарантий? Есть гарантии.
Вот пример кода, как я сейчас пишу:
var employeesRef = new ObjectReference<List<Employee>>();
var appointmentsRef = new ObjectReference<List<Appointment>>();
transactionService.executeReadOnly(() -> {
employeesRef.set(employeeRepository.queryAll());
appointmentsRef.set(appointmentRepository.queryAll());
});
var employees = employeesRef.get();
var appointments = appointmentsRef.get();
метод transactionService.executeReadOnly выполняет переданный коллбек в текущем треде и делает это ровно один раз.
Здравствуйте, vsb, Вы писали:
vsb> ·>Не очень ясно как оно должно работать. Ведь никакой гарантии, что метод лямбды будет вызван, притом ровно один раз и из того же треда. vsb> Почему нет гарантий? Есть гарантии.
Нет гарантий на уровне языка.
vsb> метод transactionService.executeReadOnly выполняет переданный коллбек в текущем треде и делает это ровно один раз.
С точки зрения JVM — завтра в transactionService подсунут другую реализацию и твой код посыпется.
Может как-то так можно:
private <T extends Runnable> T executeReadOnly(final T r)
{
// Do whatever...return r;
}
...
...
var result = transactionService.executeReadOnly(new Runnable()
{
List<Employee> employees;
List<Appointment> appointments;
public void run() {
employees = employeeRepository.queryAll();
appointments = ppointmentRepository.queryAll();
}
});
System.out.println(result.appointments);
System.out.println(result.employees);
Здравствуйте, ·, Вы писали:
vsb>> метод transactionService.executeReadOnly выполняет переданный коллбек в текущем треде и делает это ровно один раз. ·>С точки зрения JVM — завтра в transactionService подсунут другую реализацию и твой код посыпется.
Ну так можно про многое сказать. Никто там другую реализацию подсовывать не будет.
·>Может как-то так можно: ·>
·>private <T extends Runnable> T executeReadOnly(final T r)
·>{
·> // Do whatever...
·> return r;
·>}
·>...
·>...
·> var result = transactionService.executeReadOnly(new Runnable()
·> {
·> List<Employee> employees;
·> List<Appointment> appointments;
·> public void run() {
·> employees = employeeRepository.queryAll();
·> appointments = ppointmentRepository.queryAll();
·> }
·> });
·> System.out.println(result.appointments);
·> System.out.println(result.employees);
·>
Спасибо, выходит что-то вроде анонимного типа, прикольно.
Вот ещё вариант:
public static void main(String[] args) {
var x = exec(() -> new Object() {
final String a = "a";
final int b = 2;
});
System.out.println(x.a);
System.out.println(x.b);
}
static <T> T exec(Supplier<T> supplier) {
return supplier.get();
}
Получается, что до того, как в язык ввели var, такой финт был невозможен?
Здравствуйте, vsb, Вы писали:
vsb> Спасибо, выходит что-то вроде анонимного типа, прикольно.
Не что-то вроде, а он и есть.
vsb> Вот ещё вариант: vsb>
vsb> var x = exec(() -> new Object() {
vsb> final String a = "a";
vsb> final int b = 2;
vsb> });
vsb>
vsb> Получается, что до того, как в язык ввели var, такой финт был невозможен?
Да. Ибо явно приходилось указывать имя типа (в случае выше был бы Object, что никак не помогает). А у анонимного типа имени нет по определению.
Здравствуйте, vsb, Вы писали:
vsb>Заводить record некрасиво. Да, я знаю, что его даже можно внутри функции объявить. Всё равно некрасиво. У него не может быть читаемого имени. Ещё и куча писанины.
Раз у этой штуки нет имени то это code smell.
Количество кода обвязки такого вызова будет +- одинаковым, как не изгаляйся.
[b]Здравствуйте, vsb, Вы писали:
vsb>Есть ли где-нибудь в JDK уместный в данном случае класс, чтобы не писать свой?
Вообще не понял проблему. Вообще, если нужен указатель, я обычно использую AtomicReference и не парюсь оверхером, он делает что мне надо, а что он атомик — мне чаще всего пофиг.
Относительно возврата из лямбды — а какую задачу решаешь?
System.out.println(Stream.of("").map( any -> "result").findFirst().get());
any -> "result" — это вполне себе лямбда, разве нет? Еще в восьмерке это было.
Или ты что с асинхронщиной что мутишь? Тогда тебе нужен не Runnable, который ни черта не возвращает по определению, а Callable, который возвращает, и если выполняешь асинхронно то будет возвращено CompletableFuture, которое можно уже выстраивать в цепочку. Ну и всякие RxJava давно есть, если фичей поболее нужно.
vsb>> ·>Не очень ясно как оно должно работать. Ведь никакой гарантии, что метод лямбды будет вызван, притом ровно один раз и из того же треда. vsb>> Почему нет гарантий? Есть гарантии. ·>Нет гарантий на уровне языка.
vsb>> метод transactionService.executeReadOnly выполняет переданный коллбек в текущем треде и делает это ровно один раз. ·>С точки зрения JVM — завтра в transactionService подсунут другую реализацию и твой код посыпется.
·>Может как-то так можно:
Отсюда вопрос, что мешало решить проблему на уровне компилятора (генерировать временный класс, если присутствуют captured variables)?
Всё равно программист в итоге то же самое делает.
Здравствуйте, m2user, Вы писали:
M>Отсюда вопрос, что мешало решить проблему на уровне компилятора (генерировать временный класс, если присутствуют captured variables)? M>Всё равно программист в итоге то же самое делает.
Ничего не мешало, просто решили не делать и всё. Почему решили — не знаю. Вероятно это показалось слишком большим изменением, которое можно и позже добавить. Может ещё добавят.
Здравствуйте, elmal, Вы писали:
vsb>>Есть ли где-нибудь в JDK уместный в данном случае класс, чтобы не писать свой? E>Вообще не понял проблему.
Проблема простая — вернуть из лябмды более 1 значения.
> Вообще, если нужен указатель, я обычно использую AtomicReference и не парюсь оверхером, он делает что мне надо, а что он атомик — мне чаще всего пофиг.
Вариант, но мне не нравится семантика. Если я вижу AtomicReference, я предполагаю, что автор кода работал с многопоточностью, а не просто сувал первый попавшийся класс.
E>Относительно возврата из лямбды — а какую задачу решаешь?
Имеется метод для работы с транзакциями. Нужно из этого метода вернуть несколько значений.
В итоге объявил прямо в методе record с нужным числом полей и возвращаю его, как одно значение, агрегирующее остальные. Думаю, это самый подходящий вариант.
Здравствуйте, vsb, Вы писали:
vsb>Проблема простая — вернуть из лябмды более 1 значения.
Ну вообще, если нужно типизированно и анонимно, без создания нового класса, в других языках используются Tuple. К сожалению в Java действительно нормальной поддержки всего этого нет и приходится извращаться. В простейшейм случае можно конечно использовать Pair<T1, T2> — но даже для этого нет нормальной деструктуризации и пользоваться геморройно. Для трех значений можно Triple<T1, T2, T3> — в принципе при желании можно продолжить, но лучше без необходимости так не делать и возвращать явный класс с явными полями, а не абстрактные first second third и тем более далее, ибо даже в языках которые это нормально все поддерживают и имеют синтаксис для деструктуризации нужно очень аккуратно такое использовать. Потому Java way- это возвращать явный класс.
vsb>Имеется метод для работы с транзакциями. Нужно из этого метода вернуть несколько значений.
Ага, а вот тут уже проще. Ты просто декорируешь функцию по сути, у тебя на входя декоратора транзакционности Function<T, R>, на выходе будет тоже Function<T, R>. В твоем случае у тебя даже не фунция на входе, в Supplier<R>, так как взода у тебя нет,только результат.
То есть код будет:
public class Test {
public static <T> T executeReadOnly(Supplier<T> supplier) {
//do something beforevar result = supplier.get();
//do something afterreturn result
}
public static <T> Supplier<T> wrapExecuteReadOnly(Supplier<T> supplier) {
return () -> {
//do something beforevar result = supplier.get();
//do something afterreturn result;
};
}
public static void main() {
var employesToAppoitments = executeReadOnly( () -> new Pair<>("resEmplouees", "retAppoitments"));
//или так - здесь декорируемую функцию можно где сохранить и уже повторно использовать, а потом дернуть get.var employesToAppoitments1 = wrapExecuteReadOnly( () -> new Pair<>("resEmplouees", "retAppoitments")).get();
}
}
По крайней мере когда я пищу абстрактные декораторы, например добавляю транзакционность, кеширование, логирование, обработку ошибок,
повторение, пишу примерно таким образом. Либо на аспектах делаю, если уже фреймворк какой могу использовать.
PS — блин, совсем синтаксис Java забыл, так сейчас ужасают эти все new, точки с запятой в конце.
Здравствуйте, m2user, Вы писали:
M>https://www.baeldung.com/java-lambda-effectively-final-local-variables#instance-variables
volatile вообще не в тему.
M>Отсюда вопрос, что мешало решить проблему на уровне компилятора (генерировать временный класс, если присутствуют captured variables)? M>Всё равно программист в итоге то же самое делает.
"Обычное" решение не гарантирует коррекности кода. Вот такое:
int val;
f(1, x -> val = x);
assert val == 43;
...
private void f(int p, final Consumer<Integer> c) {
c.accept(42 + p);
}
Всё типа красиво. Но... теперь пришёл другой программист и поменял (не забывай, что метод может быть определён в совершенно другом месте, в какой-то либе внешней даже):
private void f(int p, final Consumer<Integer> c) {
if(x > 1) {
c.accept(42 + p);
c.accept(666 + p);
}
}
Теперь нам надо как-то изобретать правила для неинициализированных значений на стеке и правила final initialized once (т.е. если у нас объявлен final int val)... А ещё и c.accept() может выполняться из другого треда и проблемы с happens-before...
Т.е. на пустом месте усложняем ЯП, делаем кучу особых случаев, неявных дейсвтий и/или вносим лишнюю пессимизацию неявной расстановкой volatile или синхронизации например.
В общем, я считаю, что в java сделали всё правильно — максимально осторожно и явно.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, vsb, Вы писали:
vsb>>>record-ы на ровном месте городить не хочу, это точно некрасиво. GIV>>То есть чистая функция не красиво а вот функция с сайд эффектом на ровном месте красиво? GIV>>Ну ну.
vsb>Если бы в жаве были туплы, которым не надо давать имя, я бы их использовал.
Туплы — это разве не антипаттерн в бизнесовом исходном коде? Особенно когда начинаются триплы и прочие n-туплы, код становится нечитаемой кашей, потому что туплы не содержат семантики, одно только нагромождение этих элементов с бессмысленными названиями. Не станет ли код понятнее, если использовать семантический паттерн Parameter Object?
Здравствуйте, vsb, Вы писали:
vsb>В итоге объявил прямо в методе record с нужным числом полей и возвращаю его, как одно значение, агрегирующее остальные. Думаю, это самый подходящий вариант.
А чем анонимные типы-то не подошли? Вроде же неплохо работает. Что-то вроде:
var result = executeWithTxn(txn -> new Object(){
final String val1 = "val1 in " + txn;
final String val2 = "val2 in " + txn;
});
System.out.println(result.val1);
System.out.println(result.val2);
...
private <T> T executeWithTxn(final Function<String, T> action) {
return action.apply("txnId");
}
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай