Я читал на многих веб-сайтах Необязательный должен использоваться только в качестве возвращаемого типа, а не в аргументах метода. Я изо всех сил пытаюсь найти логическую причину, почему. Например, у меня есть часть логики, которая имеет 2 дополнительных параметра. Поэтому я думаю, что имеет смысл написать сигнатуру моего метода следующим образом (решение 1):
public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {
// my logic
}
Многие веб-страницы указывают, что Необязательно не следует использовать в качестве аргументов метода. Имея это в виду, я мог бы использовать следующую сигнатуру метода и добавить четкий комментарий Javadoc, чтобы указать, что аргументы могут быть нулевыми, надеясь, что будущие сопровождающие будут читать Javadoc и, следовательно, всегда выполнять нулевые проверки перед использованием аргументов (решение 2) :
public int calculateSomething(String p1, BigDecimal p2) {
// my logic
}
В качестве альтернативы я мог бы заменить свой метод четырьмя открытыми методами, чтобы обеспечить более приятный интерфейс и сделать его более очевидным, p1 и p2 являются необязательными (решение 3):
public int calculateSomething() {
calculateSomething(null, null);
}
public int calculateSomething(String p1) {
calculateSomething(p1, null);
}
public int calculateSomething(BigDecimal p2) {
calculateSomething(null, p2);
}
public int calculateSomething(String p1, BigDecimal p2) {
// my logic
}
Теперь я пытаюсь написать код класса, который вызывает этот кусок логики для каждого подхода. Сначала я получаю два входных параметра из другого объекта, который возвращает Optional
s, а затем вызываю calculateSomething
. Поэтому, если используется решение 1, вызывающий код будет выглядеть так:
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1, p2);
если используется решение 2, вызывающий код будет выглядеть так:
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));
если применяется решение 3, я мог бы использовать приведенный выше код или следующее (но это значительно больше кода):
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result;
if (p1.isPresent()) {
if (p2.isPresent()) {
result = myObject.calculateSomething(p1, p2);
} else {
result = myObject.calculateSomething(p1);
}
} else {
if (p2.isPresent()) {
result = myObject.calculateSomething(p2);
} else {
result = myObject.calculateSomething();
}
}
Итак, мой вопрос: почему считается плохой практикой использовать Optional
s в качестве аргументов метода (см. Решение 1)? Это выглядит как наиболее читаемое решение для меня и делает наиболее очевидным, что параметры могут быть пустыми / нулевыми для будущих сопровождающих. (Я знаю, что разработчики Optional
предполагали, что он будет использоваться только в качестве возвращаемого типа, но я не могу найти логических причин не использовать его в этом сценарии).
null
?Ответы:
О, эти стили кодирования должны быть приняты с небольшим количеством соли.
В общем: необязательно объединяет два состояния, которые должны быть распутаны. Следовательно, лучше подходит для результата, чем для ввода, для сложности потока данных.
источник
Optional
параметра представляет одно из трех состояний:null
необязательное, ненулевоеOptional
сisPresent() == false
и ненулевое,Optional
заключающее в себе фактическое значение.@NotNull Optional
.Optional
вообще, то состояние 1 (null
Необязательно) обычно указывает на ошибку программирования, так что вы можете также не обрабатывать ее (просто дайте ей выкинутьNullPointerException
)Лучший пост я видел на эту тему был написан Даниэлем Ольшевским :
источник
null
илиOptional
.Почти нет веских причин, чтобы не использовать в
Optional
качестве параметров. Аргументы против этого опираются на аргументы авторитета (см. Брайан Гетц - его аргумент в том, что мы не можем применять ненулевые необязательные параметры) или чтоOptional
аргументы могут быть нулевыми (по сути, тот же аргумент). Конечно, любая ссылка в Java может быть нулевой, мы должны поощрять соблюдение правил компилятором, а не памятью программистов (что проблематично и не масштабируется).Функциональные языки программирования поддерживают
Optional
параметры. Один из лучших способов использовать это - иметь несколько необязательных параметров и использоватьliftM2
для использования функции, предполагая, что параметры не пусты, и возвращая необязательные (см. Http://www.functionaljava.org/javadoc/4.4/functionaljava/fj/ data / Option.html # liftM2-fj.F- ). К сожалению, в Java 8 реализована очень ограниченная поддержка библиотек.Как программисты на Java, мы должны использовать только null для взаимодействия с устаревшими библиотеками.
источник
@Nonnull
и@Nullable
из javax.annotation вместо этого? Я широко использую их в своей библиотеке, и поддержка, предоставляемая IDE (IntelliJ), очень хорошая.@NonNull
совершенно нормально, если они служат одной и той же цели по-разному?» Для меня есть только один аргумент, который имеет хоть какой-то смысл: «Необязательно следует использовать, чтобы обеспечить более совершенные API, которые имеют четкий тип возврата. Однако для аргументов метода могут использоваться перегруженные функции». - Тем не менее, я не думаю, что это достаточно сильный аргумент.Этот совет является вариантом правила «быть как можно более неопределенным в отношении входных данных и максимально конкретным в отношении выходных данных».
Обычно, если у вас есть метод, который принимает простое ненулевое значение, вы можете отобразить его поверх
Optional
, так что простая версия является строго более неопределенной в отношении входных данных. Однако есть множество возможных причин, по которым вам все же потребуетсяOptional
аргумент:Optional
Optional
если данное значение пустоеВы думаете,Optional
это так здорово, что любой, кто использует ваш API, должен узнать об этом ;-)источник
Шаблон с
Optional
одним, чтобы избежать возвращенияnull
. Это все еще вполне возможно, чтобы перейтиnull
к методу.Хотя они пока не являются официальными, вы можете использовать аннотации в стиле JSR-308, чтобы указать, принимаете ли вы
null
значения в функцию. Обратите внимание, что вам понадобится правильный инструмент для его идентификации, и он обеспечит больше статической проверки, чем принудительная политика времени выполнения, но это поможет.источник
Optional
не решает проблему, поэтому я предлагаю аннотации.Это кажется мне немного глупым, но единственная причина, по которой я могу думать, состоит в том, что аргументы объекта в параметрах метода уже являются необязательными - они могут быть нулевыми. Поэтому заставлять кого-то брать существующий объект и оборачивать его в необязательный объект бессмысленно.
Это, как говорится, разумно делать цепочку методов, которые принимают / возвращают опционалы, например, возможно, монаду.
источник
Optional
бессмысленно ли вообще?Проверьте JavaDoc в JDK10, https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html , добавлена заметка API:
источник
Я считаю, что Optional должен быть монадой, а в Java это невозможно представить.
В функциональном программировании вы имеете дело с функциями чистого и высшего порядка, которые принимают и формируют свои аргументы только на основе своего «типа бизнес-домена». Составление функций, которые питаются или о чьих вычислениях следует сообщать в реальном мире (так называемые побочные эффекты), требует применения функций, которые обеспечивают автоматическую распаковку значений из монад, представляющих внешний мир (State, Configuration, Фьючерсы, Возможно, Либо, Писатель, и т.д ...); это называется подъемом. Вы можете думать об этом как о некоем разделении интересов.
Смешение этих двух уровней абстракции не способствует удобочитаемости, поэтому лучше избегать этого.
источник
Еще одна причина быть осторожной при передаче
Optional
параметра as заключается в том, что метод должен выполнять одну вещь ... Если вы передаетеOptional
параметр, который вы предпочитаете делать больше, чем одну вещь, это может быть похоже на передачу логического параметра.источник
if(param.isPresent())
это не лучший подход для использования Optional, вместо этого вы можете использовать:param.forEach(() -> {...})
Принятие Необязательный, поскольку параметры вызывают ненужное перенос на уровне вызывающего.
Например, в случае:
Предположим, у вас есть две ненулевые строки (т.е. возвращенные из другого метода):
Вы вынуждены обернуть их в Optional, даже если вы знаете, что они не пустые.
Это становится еще хуже, когда вам приходится сочинять с другими "отображаемыми" структурами, т.е. Либо :
ссылка:
ссылка https://github.com/teamdigitale/digital-citizenship-functions/pull/148#discussion_r170862749
источник
Я думаю, это потому, что вы обычно пишете свои функции для манипулирования данными, а затем поднимаете их для
Optional
использованияmap
и подобных функций. Это добавляетOptional
поведение по умолчанию к нему. Конечно, могут быть случаи, когда необходимо написать собственную вспомогательную функцию, которая работаетOptional
.источник
Я полагаю, что резонанс бытия заключается в том, что вы должны сначала проверить, является ли Optional нулевым, а затем попытаться оценить значение, которое он оборачивает. Слишком много ненужных проверок.
источник
Дополнительные опции не предназначены для этой цели, как хорошо объяснил Брайан Гетц .
Вы всегда можете использовать @Nullable, чтобы указать, что аргумент метода может быть нулевым. Использование опционально не позволяет вам писать логику метода более аккуратно.
источник
Еще один подход, что вы можете сделать, это
После того, как вы создали функцию (в данном случае, поставщика), вы сможете передавать ее, как и любую другую переменную, и сможете вызывать ее, используя
Идея в том, что у вас есть необязательное значение или нет, будет внутренней деталью вашей функции и не будет параметром. Мышление функционирует, когда я думаю о необязательном параметре, на самом деле это очень полезная техника, которую я нашел.
Что касается вашего вопроса, следует ли вам делать это на самом деле или нет, это зависит от ваших предпочтений, но, как говорили другие, это делает ваш API уродливым, если не сказать больше.
источник
Сначала я также предпочел передать Optionals в качестве параметра, но если вы переключитесь с перспективы API-Designer на перспективу API-User, вы увидите недостатки.
Для вашего примера, где каждый параметр является необязательным, я бы предложил изменить метод расчета на собственный класс следующим образом:
источник
Я знаю, что этот вопрос больше касается мнения, а не убедительных фактов. Но недавно я перешел от разработки .net к разработке на Java, поэтому я только недавно присоединился к группе Optional. Кроме того, я бы предпочел указать это как комментарий, но так как мой уровень баллов не позволяет мне комментировать, я вынужден вместо этого поставить это как ответ.
То, что я делаю, послужило мне хорошим эмпирическим правилом. Использовать Optional для возвращаемых типов и использовать в качестве параметров только Optional, если мне требуется как значение Optional, так и погода или нет. Optional имел значение в методе.
Если меня интересует только значение, я проверяю isPresent перед вызовом метода, если у меня есть какая-то логика или другая логика в методе, которая зависит от того, существует ли значение, тогда я с радостью передам в Optional.
источник
Это потому, что у нас разные требования к пользователю API и разработчику API.
Разработчик несет ответственность за предоставление точной спецификации и правильной реализации. Поэтому, если разработчик уже знает, что аргумент является необязательным, реализация должна правильно с ним работать, независимо от того, является ли он пустым или необязательным. API должен быть максимально простым для пользователя, а null - самым простым.
С другой стороны, результат передается от разработчика API пользователю. Несмотря на то, что спецификация является полной и многословной, существует вероятность того, что пользователь либо не знает об этом, либо просто ленится иметь с ней дело. В этом случае необязательный результат заставляет пользователя написать дополнительный код для обработки возможного пустого результата.
источник
Прежде всего, если вы используете метод 3, вы можете заменить эти последние 14 строк кода следующим:
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));
Четыре варианта, которые вы написали, являются удобными методами. Вы должны использовать их только тогда, когда они более удобны. Это также лучший подход. Таким образом, API очень ясно, какие члены необходимы, а какие нет. Если вы не хотите писать четыре метода, вы можете уточнить, как вы называете свои параметры:
public int calculateSomething(String p1OrNull, BigDecimal p2OrNull)
Таким образом, ясно, что допустимы нулевые значения.
Ваше использование
p1.orElse(null)
иллюстрирует, насколько многословен наш код при использовании Optional, поэтому я избегаю этого. Необязательно был написан для функционального программирования. Потоки это нужно. Ваши методы, вероятно, никогда не должны возвращать Optional, если в них нет необходимости использовать их в функциональном программировании. Существуют методы, например,Optional.flatMap()
method, для которых требуется ссылка на функцию, которая возвращает Optional. Вот его подпись:public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)
Так что это обычно единственная причина написать метод, который возвращает Optional. Но даже там этого можно избежать. Вы можете передать метод получения, который не возвращает Optional, методу, подобному flatMap (), обернув его в другой метод, который преобразует функцию в правильный тип. Метод обертки выглядит так:
Итак, предположим, что у вас есть такой получатель:
String getName()
Вы не можете передать его в flatMap следующим образом:
opt.flatMap(Widget::getName) // Won't work!
Но вы можете передать это так:
opt.flatMap(optFun(Widget::getName)) // Works great!
Вне функционального программирования следует избегать опций.
Брайан Гетц сказал это лучше всего, когда он сказал это:
Причина, по которой Optional был добавлен в Java, заключается в следующем:
чище, чем это:
источник