LINUX.ORG.RU

Optional недоФП

 


1

1

напал тупняк, как это сделать без временной переменной?

String keyId = Optional
    .ofNullable(jws.getKeyIdHeaderValue())
    .orElseThrow(() -> new TokenCorruptedException("no kid header"));

return Optional
    .ofNullable(store.get(keyId))
    .orElseThrow(() -> new TokenUnsignedException("unknown or expired kid: " + keyId));
★★

Ответ на: комментарий от hateyoufeel

Но ведь Optional - это и есть монада.

ТС, переписать сожно, тупо подставив текст присваивания keyId на место его использования. Но делать этого не надо - и так нормально.

anonymous
()
Ответ на: комментарий от theanonymous

да ничем не мешает, просто хотелось элегантнее ))...

тупо поставить в место использования, это не элегантно, - нечитаемая каша получается...

оставлю так наверное.

drsm ★★
() автор топика

Разупорись, зачем тебе тут опшионалы?

String keyId = jws.getKeyIdHeaderValue();
if(keyId == null) throw new TokenCorruptedException("no kid header"));
Object value = store.get(keyId);
if(value == null) throw new TokenUnsignedException("unknown or expired kid: " + keyId));
return value;

Даже строк меньше. Хочется извратится:

String keyId = jws.getKeyIdHeaderValue();
Sugar.notNull(keyId, TokenCorruptedException::new, "no kid header");
return Sugar.notNull(store.get(keyId), TokenUnsignedException::new, "unknown or expired kid: " + keyId);

что внутри Sugar.notNull догадаешься сам

Deleted
()
Ответ на: комментарий от drsm

нечитаемая каша получается...

это ты хорошо охарактеризовал код на опшионалах

Deleted
()
Ответ на: комментарий от anonymous

Ты токены посчитай.

Отстал я от ценностей настоящих программистов. Что там у вас еще модно считать?

Deleted
()
Ответ на: комментарий от Deleted

Что там у вас еще модно считать?

Не думай об этом. Просто считай строки.

anonymous
()
Ответ на: Разупорись, зачем тебе тут опшионалы? от Deleted

по поводу каши это YMMV.

ничего не имею против чтобы писать так:

String keyId = jws.getKeyIdHeaderValue();
if(keyId == null) {
    throw new TokenCorruptedException("no kid header"));
}

Object value = store.get(keyId);
if(value == null) {
    throw new TokenUnsignedException("unknown or expired kid: " + keyId));
}

return value;
когда весь код императивный, но у меня Spring WebFlux, там все reactive...

drsm ★★
() автор топика
Ответ на: комментарий от drsm

там все reactive...

только вот обмазываение опшионалами (как в ОП) не делает код реактивным

Deleted
()
Ответ на: комментарий от drsm

ничего не имею против чтобы писать так

Плохо, что не имеешь.

anonymous
()
Ответ на: комментарий от drsm

Только вот дизайн опшионала уродлив, потому единообразный стиль соответствующий.

Это все можно завернуть в одну колбасу если отказаться от двух разных исключений, или кидая RuntimeException (с ужасным империативным кодом), либо используя адекватный опшионал (или котлин).

То что наговнякали в яве, лишь убогая пародия на ФП (я не адепт оного, но всёже), не дающая ничего кроме проблем и понтов.

Deleted
()
Ответ на: комментарий от Deleted

используя адекватный опшионал (или котлин).

ну а как бы аналогичный код выглядел на Scala или Kotlin?

drsm ★★
() автор топика
Ответ на: комментарий от drsm

на котлине (по памяти)

return (jws.getKeyIdHeaderValue() ?: new TokenCorruptedException("no kid header")).run{
  store.get(this) ?: new TokenUnsignedException("unknown or expired kid: " + keyId)
}

А про скалу ты сам придумал 8)

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Deleted

Не понял что именно тебе не нравится в явовских опшинах. Вроде бы всё норм, просто работа с разнородным ИПП обычно неудобна. Если хочется пользоваться опшинами, надо заставлять свои методы возвращать опшины и писать обёртки на библиотечные.

Выглядело бы более прилично. Фокусы с неявным приведением имхо скорее шаг назад.

Правда TC-у опшины пмм не нужны, ему нужен либо аналог Either либо аналог скаловского Try и не бросать явно эксепшн, это ведь в общем то из другого стиля совсем, тем более у ТС-а там никогда пустой вариант не возвращается ведь, в опшине вообще нет смысла выходит.

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 4)

Эквивалентный код:

        return Optional.ofNullable(jws.getKeyIdHeaderValue())
                .map(keyId ->
                        Optional.ofNullable(store.get(keyId))
                                .orElseThrow(() -> new TokenUnsignedException("unknown or expired kid: " + keyId))
                ).orElseThrow(() -> new TokenCorruptedException("no kid header"));

Если не кидать разные эксепшны, можно записать проще:

        return Optional.ofNullable(jws.getKeyIdHeaderValue())
                .flatMap(keyId -> Optional.ofNullable(store.get(keyId)))
                .orElseThrow(() -> new TokenCorruptedException("no kid header"));

На мой взгляд это всё - усложнение кода на ровном месте. Пиши простой и понятный код, сравнивая с null-ами (или делай другой дизайн API возвращая Optional из своих методов).

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 1)
Ответ на: комментарий от AndreyKl

На скале как то так я бы изобразил, по памяти. Результат - Try[T] с нужным исключением, без фокусов с неявной конверсией нулл в ложь.

return (for {
  oKey <- Try(Option(jws.getKeyIdHeaderValue())).filter(_.nonEmpty) 
    orElse Failure(TokenCorruptedException("no kid header"))
  item <- Try(oKey.map{ store.byKey(_) }).filter(_.nonEmpty) 
    orElse Failure(TokenUnsignedException("unknown or expired kid: " + oKey.get))
} yield item.get)

Хотя, наверное, вариант

return Try {
  (Option(jws.getKeyIdHeaderValue()) orElse {
    throw TokenCorruptedException("no kid header")
  } flatMap { keyId =>
    Option(store.byKey(keyId)) orElse throw TokenUnsignedException("unknown or expired kid: " + keyId)
  }).get
} 

мне нравится больше. Это в общем то копия явовского и так и эдак, но так выходит как то аккуратнее немного.

AndreyKl ★★★★★
()
Ответ на: комментарий от AndreyKl

Вариант в стиле «Ява» чуть многословнее, но тоже вполне адекватен.

return Try {
  val keyId = jws.getKeyIdHeaderValue()
  if(keyId == null) {
    throw TokenCorruptedException("no kid header")
  }

  val item = store.byKey(keyId)
  if(item == null) {
    throw TokenUnsignedException("unknown or expired kid: " + keyId)
  }
}

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 2)
Ответ на: комментарий от drsm

Этот код можно назвать self-explanatory по праву, и никакой документации ненужно.

Optional.ofNullable(store.get(keyId)).orElseThrow(()

А эта конструкция вообще не интуитивна, понять её можно только после ознакомления с api Optional. Внутри метода я бы Optional не использовал, он увиличивает временные затраты на чтение кода (код больше читают чем пишут). А вот как возвращаемое значение из метода все гуд. Код опять становится self-explanatory и скажем уже не нуждается в пояснениях в javadoc, что метод может вернуть что-то или ничего, в коде уже и так это будет видно.

Aber ★★★★★
()
Ответ на: комментарий от Aber

всем спасибо, было интерeсно )

А эта конструкция вообще не интуитивна, понять её можно только после ознакомления с api Optional.

ну это грех не знать-то...

самый прикол, по поводу «читаемости», решение с Optional возникло когда наводил порядок с исключениями у себя тут, изначально код был таким:

String keyId = jws.getKeyIdHeaderValue();
KeyItem key = store.get(keyId);

if (key == null) {
    throw new UnresolvableKeyException("unknown key id: " + keyId);
}

return key;
и он не вызывал никаких вопросов... )

drsm ★★
() автор топика

Не нужно так делать. Optional был придумал для возвращение из метода null значений. Но макаки начали использовать его где попало (каюсь, сам был таким). Твой код должен выглядеть так:

String keyId = jws.getKeyIdHeaderValue()
if (keyId == null)
    throw new TokenCorruptedException("no kid header");

Key key = store.get(keyId);
if (key == null)
    throw new TokenUnsignedException("unknown or expired kid: " + keyId)

return key;

Не заставляй других материться и нервничать натягивая псевдо ФП на джаву - выглядит это убого. Если хочется ФП поставь clojure или что там сейчас модно.

foror ★★★★★
()
Ответ на: комментарий от foror

макаки начали использовать

а теперь смотри фокус

«прямое апи»

String keyId = jws.getKeyIdHeaderValue()
    .orElseThrow(() -> new TokenCorruptedException("no kid header"));

return store.get(keyId)
    .orElseThrow(() -> new TokenUnsignedException("unknown or expired kid: " + keyId));
тут ты тоже будешь материться?

«полукривоепрямое апи»

String keyId = jws.getKeyIdHeaderValue()
    .orElseThrow(() -> new TokenCorruptedException("no kid header"));

Key key = store.get(keyId);
if (key == null) {
    throw new TokenUnsignedException("unknown or expired kid: " + keyId);
}

return key;
а тут?

«мегачитаемо»

Optional<String> keyId = jws.getKeyIdHeaderValue();
if (!keyId.isPresent()) {
    throw new TokenCorruptedException("no kid header");
}

Key key = store.get(keyId);
if (key == null) {
    throw new TokenUnsignedException("unknown or expired kid: " + keyId);
}

return key;
?

drsm ★★
() автор топика
Последнее исправление: drsm (всего исправлений: 1)
Ответ на: комментарий от drsm

Я не понял про что тут, но как я и говорил назначение Optional в оборачивании null значений при возврате из метода.

Это нормальный код (для этого и придуман Optional):

String keyId = jws.getKeyIdHeaderValue()
    .orElseThrow(() -> new TokenCorruptedException("no kid header"));

return store.get(keyId)
    .orElseThrow(() -> new TokenUnsignedException("unknown or expired kid: " + keyId));

Вот это ненормальный:

String keyId = Optional
    .ofNullable(jws.getKeyIdHeaderValue())
    .orElseThrow(() -> new TokenCorruptedException("no kid header"));

return Optional
    .ofNullable(store.get(keyId))
    .orElseThrow(() -> new TokenUnsignedException("unknown or expired kid: " + keyId));

foror ★★★★★
()
Последнее исправление: foror (всего исправлений: 1)
Ответ на: комментарий от foror

Я не понял про что тут

я про случай когда часть апи возращает опшонал, а часть нулы и все это в одном методе...

drsm ★★
() автор топика
Ответ на: комментарий от drsm

Это легаси, от это никуда не деться, единственно можно сделать так:

String keyId = jws.getKeyIdHeaderValue().orElseGet(null);
if (keyId == null) {
    throw new TokenCorruptedException("no kid header");
}

Key key = store.get(keyId);
if (key == null) {
    throw new TokenUnsignedException("unknown or expired kid: " + keyId);
}

return key;
foror ★★★★★
()
Последнее исправление: foror (всего исправлений: 1)

те получается, что если ввести мандатори использование Optional.of, что эквивалентно запрету на if (something == null), то как минимум можно заставить писать правильное апи...

тк часть народу корежит... )

с контейнерами только облом со стандартными...

drsm ★★
() автор топика
Ответ на: комментарий от drsm

Можно просто использовать два стиля в одном месте и не кривить тонкой душевной организацией. Любо запилить JDK с блек джеком и путанами, параллельно порефакторив часто используемые библиотеки.

foror ★★★★★
()
Ответ на: комментарий от tailgunner

Optional<T>, пустой если нул.

drsm ★★
() автор топика
Ответ на: комментарий от foror

Можно просто использовать два стиля

можно, но если есть джуны - нельзя.

drsm ★★
() автор топика
Ответ на: комментарий от drsm

как минимум можно заставить писать правильное апи...

как минимум, у тебя скукожится GC убирать из памяти эти все опшионалы.

Deleted
()
Ответ на: комментарий от AndreyKl

Не понял что именно тебе не нравится в явовских опшинах.

То что они не нужны.

Ну т.е. нужны для затыкания null.toString() например, но это косяк языка, который «элегантно» пофиксили в котлине. Также они нужны для упоротых стримов (с коллекциями), некоторые операции которых не могут обрабатывать null значения.

Deleted
()
Ответ на: комментарий от Deleted

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

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

GC обходиться умеет, стринги всякие и прочие интегеры.

не напомнишь, для чего вообще примитивы придумали и StringBuilder (а теперь городят http://cr.openjdk.java.net/~jrose/values/values-0.html), не из-за того ли, что ты щас глупость сказал?

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Deleted

как минимум, у тебя скукожится GC убирать из памяти эти все опшионалы.

Почему скукожится

Потому что StringBuilder

/fixed

tailgunner ★★★★★
()
Ответ на: комментарий от Deleted

как минимум, у тебя скукожится GC убирать из памяти эти все опшионалы.

Думаю отработает escape analysys и все объекты будут аллоцироваться в стеке.

Aber ★★★★★
()
Ответ на: комментарий от Deleted

не напомнишь, для чего вообще примитивы придумали

В жаве много фигни придумали не к месту. Kotlin вот без примитивов как-то обошёлся.

и StringBuilder

Чтобы при конкатенации в цикле сложность была не квадратичная.

(а теперь городят http://cr.openjdk.java.net/~jrose/values/values-0.html), не из-за того ли, что ты щас глупость сказал?

Нет, не из-за того. Оптимизации оптимизациями, отказываться от правильного дизайна программы из-за микрооптимизацией это неправильно. Если в конкретном месте будут проблемы - его можно оптимизировать. А тебя послушать - так вместо Map<String, Integer> я должен искать кастомную реализацию в любом случае, ведь будут, о боже, лишние аллокации.

Legioner ★★★★★
()
Ответ на: комментарий от Deleted

Ну тогда подробней объясни, в чём проблема в нескольких лишних аллокациях, которые с 99% вероятностью не окажут никакого влияния на конечную производительность, но в то же время окажут большое влияние на качество кода. И да, которые предположительно в следующей джаве вообще станут практически бесплатными.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 1)
Ответ на: комментарий от Deleted

Ну вот есть у тебя User getUser(String id) и как ты узнаешь что оно возвращает null или кидает исключение? Самый банальный случай. Если использовать Optional, вопроса не возникает. Т.е. меньше NPE там, где забыли обработать null и меньше ненужных проверок на null там, где перестарались.

Legioner ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.