Как сделать необязательный аргумент java
Класс может содержать методы - один, два, три и больше в зависимости от сложности класса. Название метода всегда завершается двумя круглыми скобками, после которых идет блок кода, обрамлённый фигурными скобками. Например, метод sayMeow() класса Cat выводит мяуканье кошки. Внутри имени метода могут быть параметры, например, sayMeow(3) - кошка мяукает три раза. Параметров может быть несколько, тогда они разделяются запятыми.
Общая форма объявления метода выглядит следующим образом:
Метод может не иметь параметров, в этом случае используются пустые скобки. Модификатор определяет видимость метода (public, private). Если модификатор не указан, то считается, что метод имеет модификатор private в пределах своего пакета.
Методы могут вызывать другие методы.
Каждый метод начинается со строки объявления внутри круглых скобок, которую называют сигнатурой метода:
Если рассмотреть данный метод, то можно сказать следующее. Ключевое слово public означает, что метод доступен для любого класса. Ключевое слово static означает, что нам не нужно создавать экземпляр (копию) объекта Cat в памяти. Ключевое слово void означает, что метод не возвращает никаких данных. Именем метода является слово перед круглыми скобками.
Если метод возвращает какие-то данные, то в теле метода используется оператор return значение, где значение - это возвращаемое значение. Тогда вместо void нужно указать возвращаемый тип.
Вспомним наш класс Box, в котором определены высота, ширина и глубина ящика. Зная эти величины, мы вычисляли объём коробки самостоятельно. Но мы можем расширить возможности класса, чтобы он сам мог вычислить объём внутри класса и предоставить нам готовые данные. Давайте добавим в класс новый метод:
Теперь пробуем вычислить объём коробки с помощью готового метода, который есть в классе:
Мы уже не вычисляем объём вручную, за нас это сделает класс Box, у которого есть готовый метод для вычисления объёмов.
Обращение к методу осуществляется как и к переменной через точку. Наличие круглых скобок позволяет различать метод от имени переменной. То есть, если вы увидите запись:
Выше приведён немного искусственный пример, так как опытный программист никогда не назовёт переменную именем getVolume. Существует рекомендация, что для методов в начале имени нужно использовать глагол и начинаться имя должно с маленькой буквы - переменные так называть не следует.
Использование параметров
Параметры позволяют работать с различными данными. Допустим, мы хотим вычислить площадь прямоугольника со сторонами 3 и 5 см.
Метод работает, но область его применения слишком ограничена. Мы сможем вычислять площадь только одного прямоугольника с жёстко заданными размерами. Но прямоугольники бывают разными и нам нужен универсальный метод. Добиться решения задачи можно с помощью параметров. Перепишем метод следующим образом:
Теперь мы можем вычислять площадь любого прямоугольника, зная его размеры. Возьмём тот же прямоугольник со сторонами 3 и 5 см и вычислим его площадь:
В правильно написанных классах стараются избегать доступа к переменным класса напрямую. Следует использовать методы, которые позволяют избежать ошибок. В нашем классе Box использовались отдельные переменные width, height, depth. Код с использованием этих переменных слишком громоздкий, кроме того вы можете забыть про какую-нибудь переменную. Добавим в класс новый метод, который упростит наш код для вычисления объёма ящика:
Пробуем класс в действии:
Теперь мы не обращаемся к отдельным переменным класса, а вызываем метод setDim() для установки размеров ящика, после чего можно вызвать метод getVolume() для вычисления объёма. Естественно, вы можете реализовать свою логику в классе. Например, вы можете создать метод getVolume() с параметрами, которые отвечают за размеры ящика и возвращать сразу готовый результат.
Перегрузка методов
Метод - имя для действия: прыгнуть, мяукнуть, отформатировать диск. Использование имён при написании кода упрощает её понимание и модификацию. Работа разработчика схожа с работой писателя - в обоих случаях требуется донести свою мысль до читателя/приложения.
Часто одно и то же слово имеет несколько разных значений - оно перегружено. Например, мы говорим "вымыть посуду" и "вымыть кота". Было бы глупо говорить "посудовымыть посуду" или "котовымыть кота", чтобы подчеркнуть разницу. Также и с методами. Можно создавать методы с одинаковыми именами, но с разным набором аргументов.
Перегрузку (overloading) следует отличать от замещения (overriding) - иной реализации метода в подклассе первоначально определившего метод класса.
Java, как и многие языки программирования, разрешает создавать внутри одно класса несколько методов с одним именем. Главное, чтобы у них различались параметры. Параметры могут различаться типами или количеством аргументов. Будьте внимательны, если вы зададите различные типы для возвращаемого значения, то этого будет недостаточно для создания перегруженной версии метода. Когда Java встречает вызов перегруженного метода, то выбирает ту версию, параметры которой соответствуют аргументам, использованным в вызове.
Создадим класс Cat с набором перегруженных методов:
Вы можете вызвать любой метод из класса:
Если присмотреться, то можно догадаться, какая именно версия метода вызывается в каждом конкретном случае.
Аналогично, перегрузка используется и для конструкторов.
Методы с переменным числом параметров
Можно создавать методы с переменным числом параметров. В этом случае используется многоточие.
По сути, создаётся массив типа Object[].
Создадим метод, вычисляющий и возвращающий максимальное из произвольного количества значений.
До Java 5 использовался следующий способ.
Подобный код может встречаться в старых проектах, но в Android практически не используется.
Метод toString()
Каждый класс реализует метод toString(), так как он определён в классе Object. Но использовать метод по умолчанию не слишком удобно, так как не содержит полезной информации. Разработчики предпочитают переопределять данный метод под свои нужды. Сам метод имеет форму:
Вам остаётся возвратить объект класса String, который будет содержать полезную информацию о вашем классе. Давайте возьмём для примера класс Box, созданный выше:
Теперь можете узнать о классе Box:
Метод очень часто используется при создании собственных классов и вам тоже придётся прибегать к этому способу.
Как вернуть из метода больше одного значения?
Методы в Java возвращают одно значение. А если хочется сразу вернуть сразу два и более значений? Например, у нас имеется массив чисел и мы хотим написать метод, который сразу возвращает минимальное и максимальное значение из него.
Одно из решений заключается в том, что нужно "запаковать" два значения полученные в одном методе с помощью конструктора специального класса, а затем "распаковать" их в другом методе, при обращении к конструктору.
Я хочу сделать метод, который принимает 1 обязательный параметр и 1 необязательный параметр, но я нашел, как сделать необязательный массив, который делает в параметре (int. b), но это для массива, я хотите сделать это просто либо это значение равно null, либо пользователь ввел его, я могу сделать это, сделав 2 метода с тем же именем, но один с одним параметром и один с двумя параметрами, но это можно сделать только одним способом
Нет, Java не поддерживает дополнительные параметры. Другой альтернативой перегрузке (что не имеет большого значения для двух параметров, но имеет смысл для большего) - использовать тип строителя, который представляет все параметры - вы можете предоставить конструктор для строителя, который содержит требуемые параметры, а затем сеттер для каждого из необязательных, заставляя сеттер возвращать сам строитель. Поэтому вызов метода становится чем-то вроде:
То, что вы ищете, это поддержка аргументов по умолчанию. Java не обладает этой возможностью, но она более или менее имитирует эту возможность.
Один простой способ - использовать перегрузку метода.
Другой подход заключается в том, чтобы напрямую определить особые случаи, а затем заменить значения по умолчанию.
Здесь приведен пример смешивания обоих этих подходов Иво Лиммена:
Очень интересный подход, который я нашел, - использовать шаблон Design Builder. Пример здесь
Я читал на многих веб-сайтах. Необязательный должен использоваться только как возвращаемый тип, а не в аргументах метода. Я изо всех сил пытаюсь найти логическую причину. Например, у меня есть логика с двумя необязательными параметрами. Поэтому я думаю, что имеет смысл написать мою сигнатуру метода следующим образом (решение 1):
Многие веб-страницы указывают, что Optional не должен использоваться в качестве аргументов метода. Имея это в виду, я мог бы использовать следующую сигнатуру метода и добавить четкий комментарий Javadoc, чтобы указать, что аргументы могут быть нулевыми, надеясь, что будущие сопровождающие будут читать Javadoc и, следовательно, всегда выполнять нулевые проверки перед использованием аргументов (решение 2) :
В качестве альтернативы я мог бы заменить свой метод четырьмя общедоступными методами, чтобы обеспечить более приятный интерфейс и сделать его более очевидным, p1 и p2 являются необязательными (решение 3):
Теперь я пытаюсь написать код класса, который вызывает эту логику для каждого подхода. Сначала я получаю два входных параметра из другого объекта, который возвращает Optional s, а затем вызываю calculateSomething . Следовательно, если использовать решение 1, вызывающий код будет выглядеть так:
если используется решение 2, вызывающий код будет выглядеть так:
если применяется решение 3, я мог бы использовать приведенный выше код или следующий (но это значительно больше кода):
Итак, мой вопрос: почему использование Optional s в качестве аргументов метода считается плохой практикой (см. Решение 1)? Для меня это кажется наиболее читаемым решением, и для будущих сопровождающих становится наиболее очевидным, что параметры могут быть пустыми / нулевыми. (Я знаю, что разработчики Optional планировали использовать его только как возвращаемый тип, но я не могу найти никаких логических причин не использовать его в этом сценарии).
Если вы использовали опциональные параметры, разве вам не пришлось бы проверять, что необязательный параметр, переданный в качестве параметра, не является null ?
Да, но для кого-то еще, поддерживающего код в будущем, будет очевидно, что параметр может быть пустым / нулевым, поэтому потенциально можно избежать исключения нулевого указателя в будущем.
Все, что теоретически может быть нулевым, похоже на любой метод в библиотеке, который может вызвать System.exit (0). Вы не можете проверять это и не должны проверять это. Все, что вам придется делать все время, на самом деле (почти) никогда не следует делать. Вроде как сделать все параметры окончательными. Пусть ваши инструменты помогут вам предотвратить изменение значений параметров или забыть инициализировать поля вместо того, чтобы сделать ваш код нечитаемым из-за тысяч финалов и множества проверок на null.
На самом деле просто используйте аннотации NonNull / Nullable, это то, что вы ищете в этой ситуации, а не необязательно.
не совсем понимаю аргумент, что Optional <> сам может быть нулевым. Если вы установите, что в вашей кодовой базе нет нулей (вы гарантируете это на каждой границе с внешними библиотеками), то можете быть уверены, что нулей нет. Я работал с такой кодовой базой 2 года, и у нас так и не было NPE. Теперь в кодовой базе, которая хочет использовать решение 2, мы получаем NPE каждые пару недель, так что это не может быть лучше, извините. Я ручаюсь за решение 1 каждый раз. Scala делает то же самое, и никто не думает о нулях. И я тоже думаю, что Котлин
О, эти стили кодирования следует воспринимать с некоторой долей скептицизма.
- (+) Передача необязательного результата в другой метод без семантического анализа; оставив это методу, все в порядке.
- (-) Использование необязательных параметров, вызывающих условную логику внутри методов, буквально контрпродуктивно.
- (-) Необходимость упаковывать аргумент в Optional неоптимальна для компилятора и делает ненужную упаковку.
- (-) По сравнению с параметрами, допускающими значение NULL, Optional дороже.
- (-) Риск того, что кто-то передаст Optional как null в фактических параметрах.
В общем: Optional объединяет два состояния, которые необходимо распутать. Следовательно, лучше подходит для результата, чем для ввода, из-за сложности потока данных.
Фактически, наличие Optional параметра представляет одно из трех состояний: null необязательное, ненулевое Optional с isPresent() == false и ненулевое, Optional обертывающее фактическое значение.
@biziclop да, неизбежный момент уже подвергся критике. Но намерение состоит в том, чтобы иметь только ненулевые выражения. То, что совет избегать использования Optional в некоторых случаях не занимало много времени, довольно иронично. @NotNull Optional .
@biziclop Обратите внимание, что если вы Optional вообще используете, то состояние 1 ( null Необязательно) обычно указывает на ошибку программирования, поэтому вы также можете не обрабатывать ее (просто позвольте ей выбросить NullPointerException )
Лучший пост я видел на эту тему был написан Даниэлем Ольшевским :
Although it might be tempting to consider Optional for not mandatory method parameters, such a solution pale in comparison with other possible alternatives. To illustrate the problem, examine the following constructor declaration:
At first glance it may look as a right design decision. After all, we explicitly marked the attachment parameter as optional. However, as for calling the constructor, client code can become a little bit clumsy.
Instead of providing clarity, the factory methods of the Optional class only distract the reader. Note there’s only one optional parameter, but imagine having two or three. Uncle Bob definitely wouldn’t be proud of such code 😉
When a method can accept optional parameters, it’s preferable to adopt the well-proven approach and design such case using method overloading. In the example of the SystemMessage class, declaring two separate constructors are superior to using Optional.
That change makes client code much simpler and easier to read.
Кроме того, когда дело доходит до коллекций, существует явная разница между пустой коллекцией и отсутствием коллекции. Например, кеш. Это был промах в кэше? пустой необязательный / ноль. Было ли попадание в кеш, которое оказалось пустой коллекцией? полный необязательный / сборник.
Практически нет веских причин не использовать в Optional качестве параметров. Аргументы против этого основываются на аргументах авторитетных источников (см. Брайан Гетц - его аргумент в том, что мы не можем принудительно применять ненулевые необязательные параметры) или на то, что Optional аргументы могут быть нулевыми (по сути, тот же аргумент). Конечно, любая ссылка в Java может быть нулевой, нам нужно поощрять выполнение правил компилятором, а не памятью программистов (что проблематично и не масштабируется).
Как программисты на Java, мы должны использовать только null для взаимодействия с устаревшими библиотеками.
Как насчет использования @Nonnull и @Nullable из javax.annotation? Я широко использую их в разрабатываемой мной библиотеке, и поддержка, предоставляемая IDE (IntelliJ), очень хороша.
Это нормально, но вы должны использовать эту аннотацию везде, и вам нужна библиотека для поддержки таких методов, как map, flatMap / bind, liftM2, sequence и т. Д.
Функциональные языки программирования поощряют необязательные параметры. Нужна цитата. Большинство функций не должны принимать необязательные; вместо этого бремя работы (с использованием соответствующих функций высшего порядка) с наличием или отсутствием значения лежит на вызывающей стороне рассматриваемой функции.
Конечно, предпочтительнее null, но что еще предпочтительнее, так это в первую очередь не нуждаться в большом количестве необязательных значений, потому что хороший дизайн: D
Шаблон с Optional предназначен для того, чтобы избежать возврата null . Вполне возможно перейти null к методу.
Хотя они еще не являются официальными, вы можете использовать аннотации в стиле JSR-308, чтобы указать, принимаете ли вы null значения в функцию. Обратите внимание, что у вас должен быть правильный инструмент для его фактической идентификации, и он обеспечит больше статической проверки, чем принудительная политика времени выполнения, но это поможет.
Проблема здесь скорее в том, что @NotNull не является случаем по умолчанию и должен быть явно аннотирован - следовательно, шаблон и прочее, что люди просто не будут делать из-за лени.
Игнорировать необязательный параметр намного сложнее, чем игнорировать аннотацию, которую вы заметите только при чтении документации / источника или если ваш инструментарий может ее поймать (статический анализ не всегда может правильно определить допустимость значений NULL).
Обычно, если у вас есть метод, который принимает простое ненулевое значение, вы можете сопоставить его с Optional , так что простая версия строго более неспецифична в отношении входных данных. Однако существует множество возможных причин, по которым вам все же может потребоваться Optional аргумент:
- вы хотите, чтобы ваша функция использовалась вместе с другим API, который возвращает Optional
- Ваша функция должна возвращать что-то кроме пустого, Optional если заданное значение пусто
Вы думаете, что Optional это настолько круто, что любой, кто использует ваш API, должен знать об этом ;-)
Давайте проясним кое-что: в других языках нет общих рекомендаций против использования типа Maybe в качестве типа поля, типа параметра конструктора, типа параметра метода или типа параметра функции.
Рассуждения, которые могут относиться к другим типам Maybe или другим языкам, здесь, вероятно, неверны.
[W]e did have a clear intention when adding [Optional], and it was not to be a general purpose Maybe type, as much as many people would have liked us to do so. Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors.
For example, you probably should never use it for something that returns an array of results, or a list of results; instead return an empty array or list. You should almost never use it as a field of something or a method parameter.
Тем не менее, на практике я редко сталкивался с проблемой использования Optional в качестве типа поля или типа параметра. Если Optional, несмотря на свои ограничения, работает как тип параметра или тип поля для вашего варианта использования, используйте его.
Мне это кажется немного глупым, но единственная причина, по которой я могу думать, это то, что аргументы объекта в параметрах метода уже в каком-то смысле необязательны - они могут быть нулевыми. Поэтому принуждать кого-то взять существующий объект и заключить его в необязательный вид бессмысленно.
При этом разумно объединить в цепочку методы, которые принимают / возвращают опции, например, монада Maybe.
Не могут возвращаемые значения и поля также быть нулевыми? Так что Optional это совершенно бессмысленно?
API Note: Optional is primarily intended for use as a method return type where there is a clear need to represent "no result," and where using null is likely to cause errors.
Я считаю, что Optional должен быть Monad, а это невозможно в Java.
Смешение этих двух уровней абстракции не способствует удобочитаемости, поэтому лучше просто избегать этого.
Принятие необязательных параметров в качестве параметров приводит к ненужному переносу на уровне вызывающего объекта.
Например, в случае:
Предположим, у вас есть две ненулевые строки (т.е. возвращенные другим методом):
Вы вынуждены заключить их в Optional, даже если вы знаете, что они не Пустые.
methods shouldn't expect Option as parameters, this is almost always a code smell that indicated a leakage of control flow from the caller to the callee, it should be responsibility of the caller to check the content of an Option
Возможно, я спровоцирую кучу голосов против и отрицательных комментариев, но . Я терпеть не могу.
Отказ от ответственности: то, что я пишу ниже, на самом деле не является ответом на исходный вопрос, а скорее моими мыслями по теме. И единственный источник для этого - мои мысли и мой опыт (с Java и другими языками).
Сначала давайте проверим, зачем вообще кому-то нужно использовать Optional?
Для меня причина проста: в отличие от других языков java не имеет встроенной возможности определять переменную (или тип) как допускающую значение NULL или нет. Все "объектные" -переменные допускают значение NULL, а все примитивные типы - нет. Для простоты давайте не будем рассматривать примитивные типы в дальнейшем обсуждении, поэтому я просто буду утверждать, что все переменные допускают значение NULL.
Зачем нужно объявлять переменные как допускающие / не допускающие значение NULL? Что ж, причина для меня в том, что явное всегда лучше, чем неявное. Помимо явного оформления (например, аннотации или типа), может помочь статическому анализатору (или компилятору) выявить некоторые проблемы, связанные с нулевым указателем.
Многие люди утверждают в комментариях выше, что функциям не обязательно иметь аргументы, допускающие значение NULL. Вместо этого следует использовать перегрузки. Но такое утверждение годится только в школьной тетради. В реальной жизни бывают разные ситуации. Рассмотрим класс, который представляет настройки какой-либо системы или личные данные какого-то пользователя, или фактически любую составную структуру данных, которая содержит множество полей - многие из них имеют повторяющиеся типы, а некоторые поля являются обязательными, а другие - необязательными. . В таких случаях перегрузки наследования / конструктора не помогают.
Случайный пример: допустим, нам нужно собрать данные о людях. Но некоторые люди не хотят предоставлять все данные. И, конечно же, это POD, так что в основном тип с семантикой значений, поэтому я хочу, чтобы он был более или менее неизменным (без сеттеров).
Итак, обсуждение IMO выше показывает, что хотя в большинстве случаев мы можем выжить без аргументов, допускающих значение NULL, но иногда это действительно невозможно.
Теперь мы подходим к проблеме: если некоторые аргументы допускают значение NULL, а другие - нет, как мы узнаем, какой из них?
Подход 1. Все аргументы допускают значение NULL (в соответствии со стандартом Java, кроме примитивных типов). Итак, мы проверяем их все.
Подход 2: Используйте документацию и / или комментарии, чтобы описать, какие аргументы / поля допускают значение NULL, а какие нет.
Результат: не работает толком. Людям лень писать и читать документы. Кроме того, в последнее время наблюдается тенденция избегать написания документации в пользу того, чтобы код сам описывал себя. Кроме того, все рассуждения об изменении кода и забывании модификации документации остаются в силе.
Подход 3: @Nullable @NonNull и т. Д. Я лично считаю их хорошими. Но есть определенные недостатки: (например, они соблюдаются только внешними инструментами, а не компилятором), худший из которых заключается в том, что они не являются стандартными, что означает, что 1. Мне нужно было бы добавить внешнюю зависимость к моему проекту, чтобы получить выгоду. от них, и 2. То, как они обрабатываются в разных системах, неодинаково. Насколько мне известно, они были отклонены от официального стандарта Java (и я не знаю, есть ли какие-либо планы попробовать еще раз).
Подход 4: Необязательный <>. Недостатки уже упомянуты в других комментариях, худшим из которых является снижение производительности (IMO). Также он добавляет немного шаблонов, хотя я лично считаю, что использование Optional.empty () и Optional.of () не так уж и плохо. Преимущества очевидны:
- Это часть стандарта Java.
- Читателю кода (или пользователю API) становится очевидным, что эти аргументы могут быть нулевыми. Более того, он заставляет как пользователя API, так и разработчика метода узнать об этом факте, явно упаковывая / разворачивая значения (что не так, когда используются аннотации, такие как @Nullable и т. Д.).
Итак, на мой взгляд, в отношении любой методологии, в том числе и этой, нет однозначных выводов. Я лично пришел к следующим рекомендациям и соглашениям (которые все еще не являются строгими правилами):
- Внутри моего собственного кода все переменные не должны быть нулевыми (но, вероятно, Optional <>).
- Если у меня есть метод с одним или двумя необязательными аргументами, я пытаюсь переделать его, используя перегрузки, наследование и т. Д.
- Если я не могу найти решение в разумные сроки, я начинаю думать, критична ли производительность (т.е. если есть миллионы объектов, которые нужно обработать). Обычно это не так.
- Если нет, я использую Optional в качестве типов аргументов и / или типов полей.
По-прежнему существуют серые области, в которых эти условные обозначения не работают:
- Нам нужна высокая производительность (например, обработка огромных объемов данных, так что общее время выполнения очень велико, или ситуации, когда пропускная способность критична). В этом случае снижение производительности, вносимое Optional, может быть действительно нежелательным.
- Мы находимся на границе кода, который пишем сами, например: мы читаем из БД, конечную точку отдыха, файл синтаксического анализа и т. Д.
- Или мы просто используем какие-то внешние библиотеки, которые не соответствуют нашим соглашениям, поэтому, опять же, мы должны быть осторожны .
Кстати, последние два случая тоже могут быть источником необходимости в необязательных полях / аргументах. Т.е. когда структура данных не разрабатывается нами, а навязывается какими-то внешними интерфейсами, db-схемами и т. Д.
В конце, я считаю, что нужно подумать над проблемой, которая решается, и попытаться найти соответствующие инструменты. Если вариант Optional <> подходит, я не вижу причин не использовать его.
Я хочу сделать метод, который принимает 1 обязательный параметр и 1 необязательный параметр, но я нашел, как сделать необязательный массив, который в параметре (int. b), но это для массива, я хочу сделать просто либо это значение равно нулю, либо пользователь ввел его, я могу сделать это, сделав 2 метода с одинаковым именем, но один с одним параметром и один с 2 параметрами, но можно ли это сделать только одним методом?
Нет, Java не поддерживает необязательные параметры. Другой альтернативой перегрузке (которая не имеет большого смысла для двух параметров, но имеет смысл для большего) является использование типа компоновщика, который представляет все параметры - вы можете предоставить конструктор для компоновщика, который содержит требуемые параметры, а затем установщик для каждого из дополнительных, заставляя установщик возвращать сам строитель. Так что вызов метода становится примерно таким:
То, что вы ищете, это поддержка аргументов по умолчанию. У Java нет такой возможности, но она более или менее имитирует эту возможность.
Одним простым способом является использование перегрузки метода.
Другой подход заключается в непосредственном выявлении особых случаев и последующей замене значений по умолчанию.
Вот пример смешивания обоих подходов Иво Лиммена:
Очень интересный подход, который я нашел, заключается в использовании шаблона Design Builder. Здесь есть пример
Читайте также: