Содержание

Руководство по Java Core. Базовые операторы. – PROSELYTE

Для манипулирования переменными в языке Java предусмотренно множество операторов. Все операторы мы модем поделить на такие группы:

  • Арифметические операторы
  • Операторы сравнения
  • Битовые операторы
  • Логические операторы
  • Операторы присваивания
  • Другие

Рассмотрим эти группы отдельно.


Арифметические операторы

Этот тип операторов используется для математических операций.

Представим, что у нас есть две целочисленные переменные (int) A = 50, B = 20.

Примеры:

+ (Сложение) A + B = 70
– (Вычитание)A – B = 30
* (Умножение)A * B = 1000
/ (Деление)A / B = 2
% (Остаток от деления)A % B = 10
++ (Инкремент)A++ = 51
— (Декремент)B– = 19

Операторы сравнения

Результатом оператора сравнения является логический тип boolean, который говорит нам, является ли данное утверждение правдой (true) или ложью (false).

Представим, что у нас есть две целочисленные переменные (int) A = 50, B = 20.

Примеры:

== (Равен) (A == B) -> false
!= (Не равен)
(A != B) -> true
> (Больше, чем)(A > B) -> true
< (Меньше, чем)(A < B) -> false
>= (Больше, чем или равно)(A >= B) -> true
<= (Меньше, чем или равно)(A <= B) -> false

Битовые операторы

В языке Java существует ряд битовых операторов, которые поддерживают целочисленные типы данных.

Битовые операторы позволяют нам выполнять побитовые операции.

Представим, что у нас есть две целочисленные переменные (int) A = 50, B = 20. B) -> 0010 0110~ (Побитовое дополнение)( ~A ) -> 1100 1101
<< (Побитовый сдвиг влево)(A << 1) -> 100 (0110 0100)
>> (Побитовый сдвиг вправо)(B >> 2) -> 5 (0000 0101)

>>> (Побитовый сдвиг вправо с присвоением)
( A >>> 3) -> 6 (0000 0110)

Логические операторы

В языке Java поддерживаются 3 логических оператора, все они приведены таблице ниже.

Представим, что у нас есть две логические переменные (boolean) T = true и F = false.

&& (Логическое ‘И’)    (A && B) -> false
|| (Логическое ‘ИЛИ’)   (A || B) -> true
! (Логическое ‘НЕ’)
   !(A || B) -> false

Операторы присваивания

В языке Java есть 11 операторов присваивания. Рассмотрим их на примерах.

Представим, что у нас есть две целочисленные переменные (int) A = 50, B = 20 и некая переменная С, значение которой, пока не присвоено.

=
   С = A + B ( C == 70 )
+=   A += B (A == 70)
-=
   A -= B (A == 30)
*=   B *= A (B == 1000)
/=   A /= B (A == 2)
%=
   A %= B (A == 10)
<<=
   B <<= 2 (B == 80)
>>=
   A >>= 2 (A == 12)
&=   B &= 2 (B = B & 2 )
^=
   A ^= 2 (A == 48)
|=   A |= 3 (A == 51)

Другие

К другим операторам в языке Java относится только 1. Это тернарный оператор ветвления IF.

Более подробно мы рассмотрим IF далее, а пока приведём общую форму записи:

Допустим у нас есть логическая переменная (boolean) isActive, значение которой нам неизвестно.

Пример:

int a = (isActive) ? 100 : 200.

Разъяснение:

Если isActive = true, тогда a = 100.

Если isActive = false, тогда a = 200.


В этом уроке мы рассмотрели операторы для манипулирования переменными в языке Java.

Более подробно с операторами в языке Java можно ознакомиться в спецификации языка Java (Java Language Specification).

В следующем уроке мы рассмотрим массивы в языке Java и их виды.

Ключевые слова и операторы — Kotlin


Жесткие ключевые слова (Hard Keywords)

Следующие слова всегда интерпретируются как ключевые и не могут использоваться в качестве идентификаторов:

  • as
    • используется для приведения типов
    • задает альтернативное имя для импорта
  • as? используется для безопасного приведения типов
  • break завершает выполнение цикла
  • class объявляет класс
  • continue переходит к следующему шагу ближайшего вложенного цикла
  • do начинает цикл do/while loop (цикл с постусловием)
  • else определяет ветвь выражения if, которое выполняется, когда условие ложно
  • false указывает значение ‘false’ типа Boolean
  • for начинает цикл for
  • fun объявляет функцию
  • if начинает условное выражение if
  • in
    • указывает объект, который перебирается в цикле for
    • используется как инфиксный оператор для проверки того, что значение принадлежит диапазону, коллекции или другого объекта, который определяет метод ‘contains’
    • используется в выражениях when с той же целью
    • отмечает параметр типа как контравариантный
  • !in
    • используется в качестве оператора для проверки того, что значение не принадлежит диапазону, коллекции или другой объекта, который определяет метод ‘contains’
    • используется в выражениях when с той же целью
  • interface объявляет интерфейс
  • is
    • проверяет, что значение имеет определенный тип
    • используется в выражениях when с той же целью
  • !is
    • проверяет, что значение не имеет определенный тип
    • используется в выражениях when с той же целью
  • null константа, представляющая ссылку на объект, которая не указывает ни на один объект
  • object объявляет класс и его экземпляр одновременно
  • package указывает пакет для текущего файла
  • return по умолчанию производит возврат из ближайшей окружающей его функции или анонимной функции
  • super
    • ссылается на реализацию суперкласса метода или свойства
    • вызывает конструктор суперкласса из вторичного конструктора
  • this
    • относится к текущему приемнику
    • вызывает другой конструктор того же класса из вторичного конструктор
  • throw вызывает исключение
  • true задает значение ‘true’ типа Boolean
  • try начинает блок обработки исключений
  • typealias объявляет псевдоним типа
  • val объявляет свойствотолько для чтения или локальную переменную
  • var объявляет изменяемое свойство или локальную переменную
  • when начинает выражение when (выполняет одну из заданных ветвей)
  • while начинает цикл while (цикл с предусловием)

Мягкие Ключевые Слова (Soft Keywords)

Следующие слова действуют как ключевые в контексте, когда они применимы и могут использоваться как идентификаторы в других контекстах:

  • by
    • делегирует реализацию интерфейса другому объекту
    • делегирует реализацию методов доступа для свойства другому объекту
  • catch начинает блок, который обрабатывает определенный тип исключения
  • constructor объявляет первичный или вторичный конструктор
  • delegate используется в качестве целевого объекта аннотации
  • dynamic ссылается на динамический тип в Kotlin/JS коде
  • field используется в качестве целевого объекта аннотации
  • file используется в качестве целевого объекта аннотации
  • finally начинает блок, который всегда выполняется при выходе из блока try
  • get
    • объявляет геттер свойства
    • используется в качестве целевого объекта аннотации
  • import импортирует объявление из другого пакета в текущий файл
  • init начинает блок инициализации
  • param используется в качестве целевого объекта аннотации
  • property используется в качестве целевого объекта аннотации
  • receiverиспользуется в качестве целевого объекта аннотации
  • set
    • объявляет сеттер свойства
    • используется в качестве целевого объекта аннотации
  • setparam используется в качестве целевого объекта аннотации
  • where задает ограничения для параметра универсального типа

Модификаторы (Modifier Keywords)

Следующие слова действуют как ключевые в списках модификаторов объявлений и могут использоваться как идентификаторы в других контекстах:

  • actual означает реализацию Платформы в мультиплатформенных проектах
  • abstract обозначает класс или элемент как абстрактный
  • annotation объявляет класс аннотации
  • companion объявляет объект-компаньон
  • const помечает свойство как константу времени компиляции
  • crossinline запрещает нелокальные возвраты в лямбде, передаваемом встроенной функции
  • data указывает компилятору генерировать канонические элементы для класса
  • enum объявляет перечисление
  • expect отмечает объявление как платформенное, ожидая реализации в модулях платформы.
  • external отмечает объявление как реализованное не в Kotlin (доступное через JNI или JavaScript)
  • final запрещает переопределение элемента
  • infix позволяет вызывать функцию в инфиксной записи
  • inline указывает компилятору встроить функцию и лямбда-выражение на стороне вызова
  • inner позволяет ссылаться на экземпляр внешнего класса из вложенного класса
  • internal помечает объявление как видимое в текущем модуле
  • lateinit позволяет инициализировать не-null свойство вне конструктора
  • noinline отключает подстановку лямбды, переданной во встроенную функцию
  • open позволяет создавать подклассы класса или переопределять элемент
  • operator обозначает функцию как перегрузку оператора или реализацию соглашения
  • out обозначает тип параметра как ковариантный
  • override помечает элемент как переопределение элемента суперкласса
  • private помечает объявление как видимое в текущем классе или файле
  • protected помечает объявление как видимое в текущем классе и его подклассах
  • public помечает декларацию как видимую в любом месте
  • reified обозначает параметр типа встроенной функции, как доступная во время выполнения
  • sealed объявляет изолированный класс (класс с ограниченным подклассом)
  • suspend обозначает функцию или лямбда как приостанавливаемую (используется как сопрограмма)
  • tailrec отмечает функцию как с хвостовой рекурсией (позволяя компилятору заменить рекурсию итерацией)
  • vararg позволяет передавать нефиксированное число аргументов для параметра

Специальные идентификаторы (Special Identifiers)

Следующие идентификаторы определяются компилятором в определенных контекстах и могут использоваться как обычные идентификаторы в других контекстах:

  • field используется внутри метода доступа свойства для ссылки на backing field свойства
  • it используется внутри лямбды, чтобы косвенно ссылаться на ее параметр

Операторы и специальные символы (Operators and Special Symbols)

Котлин поддерживает следующие операторы и специальные символы:

  • +, -, *, /, % — математические операторы
    • * также используется для передачи массива в параметр vararg
  • =
    • оператор присваивания
    • используется для указания значений по умолчанию для параметров
  • +=, -=, *=, /=, %= — расширенные операторы присваивания
  • ++, -- — операторы инкремента и декремента
  • &&, ||, ! — логические операторы ‘и’, ‘или’, ‘не’ (для побитовых операций используют соответствующие инфиксные функции)
  • ==, != — операторы равенства (переведенные на вызовы equals() для не-примитивных типов)
  • ===, !== — операторы ссылочного равенства
  • <, >, <=, >= — операторы сравнения (переведенные на вызовы compareTo() для не-примитивных типов)
  • [, ] — оператор индексированного доступа (переведенный на вызовы get и set)
  • !! утверждает, что выражение не равно null
  • ?. выполняет безопасный вызов (вызывает метод или обращается к свойству, если получатель не имеет значения null)
  • ?: принимает правое значение, если левое значение равно нулю (Элвис оператор)
  • :: создает ссылку на элемент или ссылку на класс
  • .. создает диапазон
  • : отделяет имя от типа в объявлениях
  • ? отмечает тип с нулевым значением
  • ->
    • разделяет параметры и тело лямбда-выражения
    • разделяет параметры и тип возвращаемого значения при объявлении лямбды
    • разделяет условие и тело ветви выражения when
  • @
    • вводит аннотацию
    • вводит или ссылается на метку цикла
    • вводит или ссылается на лямбда-метку
    • ссылается на выражение ‘this’ из внешней области
    • ссылается на внешний суперкласс
  • ; разделяет несколько операторов на одной строке
  • $ ссылается на переменную или выражение в строковом шаблоне
  • _
    • заменяет неиспользуемый параметр в лямбда выражении
    • заменяет неиспользуемый параметр в деструктуризирующем присваивании

Действительно ли == работает одинаково или по-разному при сравнении двух примитивов и двух объектов в Java? [java, object, primitive, equals-operator]


При поиске объяснений того, как логическое равенство == работает в Java, ответы всегда примерно такие:

  • Для примитивов он возвращает, имеют ли примитивы одинаковое значение (это включает в себя сравнение примитива с его WrapperObject, поскольку WrapperObject автоматически распаковывается в примитив).
  • Для объектов он возвращает, представляют ли они один и тот же объект в куче.

Но все эти объяснения, кажется, подразумевают, что это две разные вещи, что == ведет себя по-разному в зависимости от того, сравниваете ли вы объекты с примитивами. Мне кажется, что на самом деле они должны быть одно и то же: взять две переменные из стека и сравнить их значения.

Меняется не поведение ==, а то, что представляют сравниваемые значения. Если вещи, которые вы сравниваете, являются примитивами, тогда значение в стеке является значением самого примитива. Если вы сравниваете объекты, то значение в стеке является значением ссылки (и, следовательно, адресом объекта в куче).

Я что-то неправильно понял, или == действительно ведет себя одинаково во всех ситуациях? Бонусные баллы, если вы можете указать мне на документацию о том, как это действительно работает под прикрытием.


java object primitive equals-operator

person user7382368    schedule 18. 08.2019    source источник



Ответы (2)

arrow_upward
18
arrow_downward

Как говорят другие ответы/комментарии, на уровне языка Java семантика оператора == указана (в JLS 15.21) независимо от реализации. Строго говоря, вы не можете вывести детали реализации «под капотом» из текста JLS. Все, что вы можете сказать, это то, что любая совместимая реализация == должна вести себя определенным образом.

Я предполагаю, что мы говорим об обычных JVM, где фактическим машинным представлением ссылки является машинный адрес. Можно реализовать ссылки другими способами; например, используя какой-либо механизм косвенной адресации, такой как PIDLAM.

На уровне байт-кода существует ряд различных инструкций байт-кода, которые реализуют логику == в зависимости от типа (int, long или ссылка). Однако семантика сравнений аналогична. Как только байт-коды будут проверены на безопасность типов, целые числа и адреса могут обрабатываться одинаково для целей == сравнения на аппаратном уровне.

На уровне аппаратных средств (машинных инструкций) == работает одинаково для примитивных целочисленных типов и непримитивных значений. В обоих случаях будет выполняться машинная инструкция, которая сравнивает два «слова», взятых из регистра или из памяти (кучи или стека).


Специфицированная JLS семантика == для float и double немного отличается, поскольку специальные значения (бесконечности и нечисловые значения) требуют специальной обработки. Например: NaN == NaN равно false. См. также стандарт IEEE 754 с плавающей запятой.

Для этого существуют разные байт-коды, и на аппаратном уровне используемые инструкции отличаются от тех, которые используются в целочисленных и эталонных случаях. (Обработка специальных значений обычно выполняется на плавающем оборудовании.)


Определенная JLS семантика == для boolean, byte, short и char заключается в преобразовании значений в другой тип (int, long, float или double) перед их сравнением. Продвижение происходит и в других случаях, если операнды имеют разные (неупакованные) типы.

Кроме того, распаковка происходит, если упакован один (но не оба!) операнда. Если оба операнда упакованы, то == является эталонным сравнением.


Резюмируя вышеизложенное…

Я что-то неправильно понял или == действительно ведет себя одинаково во всех ситуациях?

Нет, если вы включаете типы с плавающей запятой и соображения расширения и распаковки примитивов.

Бонусные баллы, если вы можете указать мне на документацию о том, как это действительно работает под прикрытием.

Для этого нет официальной (Oracle) общедоступной документации. Спецификация JLS и JVM не предписывает стратегии реализации.

person Stephen C    schedule 18.08.2019


arrow_upward
4
arrow_downward

Я понимаю ваше объяснение, и оно верно с учетом некоторых определений терминов. Но это не соответствует тому, как Java говорит об объектах и ​​примитивах.

Идея «ссылки» на объект в Java серьезно преуменьшается; Я думаю, что можно быть «Java-программистом» и не совсем понимать, что такое ссылка. Вы можете запомнить правила, в которых это имеет значение — оператор ‘==’, передача параметров методам — и не понимать, как это реализовано или могло бы быть реализовано.

Поэтому я думаю, что многих людей, которые программируют на Java, смутило бы, если бы они сказали, что == «ведет себя одинаково во всех ситуациях», потому что это требует слишком много знаний «под прикрытием». Java не поощряет (и не требует) заглядывать «под одеяло» до такой степени.

person arcy    schedule 18.08.2019

[Глава 4] 4.9 Операторы сравнения равенства

Глава 4
Выражения


Равенство операторы сравнения в Java используются для равенства (==) и операции сравнения не-равно (!=). Операторы сравнения равенства могут появляться в выражении равенства:

операторы сравнения на равенство равны по приоритету и оцениваются слева направо. == и != операторы сравнения могут выполнять числовые сравнения, логические сравнения и сравнения ссылочного типа. Оба этих оператора производить логические значения.

Каталожные номера операторы реляционного сравнения; Порядок операций

Оператор эквивалентности ==

Оператор равенства == выполняет сравнение между своими операндами и возвращает логическое значение. Возвращает чистое значение true, если операнды равны друг другу; в противном случае возвращается чистое значение false. Оператор == может появляться как часть равенства выражение. Оператор равенства никогда не генерирует исключение.

Операнды == могут быть любого типа, но они оба должны быть одного типа или времени компиляции возникает ошибка. Если один операнд имеет арифметический тип, то другой также должны быть арифметического типа. Если один операнд имеет тип boolean, другой также должен быть логического типа. Если один операнд является ссылочным типом, другой также должен быть ссылочным тип. Обратите внимание, что ни один из операндов не может быть выражением, вызывающим метод пустоты.

Если оба операнда имеют арифметические типы, то оператор выполняет арифметическое равенство сравнение. Оператор может выполнять преобразования типов операндов:

  • Если любой из операндов имеет тип double, то другой операнд преобразуется в двойной.
  • В противном случае, если один из операндов имеет тип float, другой операнд преобразуется в число с плавающей запятой.
  • В противном случае, если один из операндов имеет тип long, другой операнд преобразуется в длинный.
  • В противном случае, оба операнда преобразуются в int.

сравнение на равенство любых двух арифметических значений дает истину тогда и только тогда, когда оба операнда имеют одно и то же значение; иначе сравнение выдает ложное. Сравнение с плавающей запятой данные регулируются следующими дополнительными правилами:

  • Если любой из операндов не является числом (NaN), сравнение дает ложь.
  • Положительная бесконечность — это отдельное значение, равное самому себе, и не равно никакому другому значению.
  • Отрицательная бесконечность это отдельное значение, равное самому себе и не равное никакому другое значение.
  • Положительный и отрицательный нуль обрабатываются как равные, поэтому -0.0==0.0 дает истину.

Если оба операнда являются булевыми значениями, оператор выполняет логическое сравнение на равенство. Сравнение возвращает истину, если оба операнда истинны или оба операнда ложны. В противном случае сравнение выдает ложное.

Если оба операнда являются ссылочными типами, оператор выполняет сравнение объектов на равенство. Для выполнения этого типа сравнения должна быть возможность привести значение одного из операндов к типу другого операнд, или возникает ошибка времени компиляции. Сравнение дает истина, если оба его операнда относятся к одному и тому же объект или если оба его операнда равны нулю; в противном случае сравнение дает ложь.

Потому что оператор == определяет, являются ли два объекта один и тот же объект, он не подходит для сравнений, которые требуют чтобы определить, имеют ли два объекта одинаковое содержимое. Например, если вам нужно знать, являются ли два объекта String содержат одинаковые последовательности символов, == оператор не подходит. Вы должны использовать equals() вместо этого метод:[4]

[4] Это похоже на разницу в C между записью string1==string2 и strcmp(строка1, строка2)==0.

string1.equals (string2) // Сравнивает содержимое строк
string1 == string2 // Сравнивает фактические строковые объекты
 

Каталожные номера арифметические типы; логический тип; Ссылочные типы

Неравный оператор !=

Оператор не равно != выполняет сравнение между своими операндами и возвращает логическое значение ценность. Возвращает чистое значение true, если операнды не равны друг другу; в противном случае он возвращает чистый значение ложно. Оператор != может появляться как часть выражения равенства. Оператор не равно никогда не выдает исключение.

Операнды != может быть любого типа, но они оба должны быть одного типа или возникает ошибка времени компиляции. Если один операнд имеет арифметическое значение тип, другой также должен быть арифметического типа. Если один операнд имеет тип boolean, другой также должен быть типа тип булев. Если один операнд является ссылочным типом, другой также должен быть ссылочным типом. Обратите внимание, что ни один из операндов может быть выражением, которое вызывает метод void.

Если оба операнда имеют арифметический тип, оператор выполняет сравнение арифметического неравенства. Оператор может выполнять тип преобразования операндов:

  • Если любой из операндов имеет тип double, тогда другой операнд преобразуется в двойной.
  • В противном случае, если любой операнд имеет тип float, другой операнд преобразуется держаться на плаву.
  • В противном случае, если любой операнд имеет тип long, другой операнд преобразуется долго.
  • В противном случае оба операнда преобразуются в внутр.

Неравенство сравнение любых двух арифметических значений дает истинное тогда и только тогда, когда оба операнда не являются одним и тем же значением; иначе сравнение дает ложь. Сравнение данные с плавающей запятой регулируются следующими дополнительными правилами:

  • Если какой-либо из операндов не является числом (NaN), сравнение дает истинный. NaN — единственное значение, которое сравнивается с не равно себе.
  • Положительная бесконечность является отличным значение, равное самому себе и не равное никакому другому значению.
  • Отрицательная бесконечность — это отдельное значение, равное самому себе, и не равно никакому другому значению.
  • Положительный и отрицательный нули рассматриваются как равные, поэтому -0.0!=0.0 дает ЛОЖЬ.

Если оба операнда логические значения, оператор выполняет сравнение логических неравенств. сравнение дает false, если оба операнда истина или оба операнда ложны. В противном случае сравнение дает true.

Если оба операнда являются ссылочными типами, оператор выполняет сравнение объектов на равенство. Для выполнения этого вида сравнения, должна быть возможность привести значение одного из операнды к типу другого операнда или ошибка времени компиляции имеет место. Сравнение дает истину, если оба его операндов относятся к разным объектам, и если оба его операнда не являются нулевыми; в противном случае сравнение дает ЛОЖЬ.

Потому что != оператор определяет, являются ли два объекта разными объектами, это не подходит для сравнений, которые должны определить, являются ли два объекта имеют разное содержание. Например, если вам нужно узнать, два объекта String содержат разные последовательности символов, оператор != не подходит. Вы должны использовать equals() вместо этого метод: [5]

[5] Это похоже на разницу в C между записью string1!=string2 и strcmp(строка1, строка2)!=0.

!string1.equals (string2) // Сравнивает содержимое строк
string1 != string2 // Сравнивает фактические строковые объекты
 

Каталожные номера арифметические типы; логический тип; Ссылочные типы


Операторы сравнения отношений Побитовые/логические операторы

[Глава 4] 4.

8 Операторы реляционного сравнения

Относительный операторы сравнения в Java используются для менее чем (<), меньше или равно (<=), больше или равно (>=), больше (>), и instanceof операции сравнения. Они может появиться в относительном выражении:

операторы реляционного сравнения имеют равный приоритет и оценивается слева направо. <, <=, Операторы >= и > являются числовыми операторы сравнения, а instanceof — это тип оператор сравнения. Все эти операторы производят логические ценности.

Каталожные номера Операторы смены; Порядок операций; Тип 3

Меньший оператор

<

Оператор меньше чем < выполняет сравнение между своими операндами и возвращает логическое значение ценность. Он возвращает чистое значение true, если его левый операнд меньше правого операнда; в противном случае оператор возвращает чистое значение false. < оператор может появляться как часть реляционного выражения. оператор меньше чем никогда не генерирует исключение.

Типы оба операнда оператора «меньше» должны быть арифметическими типами, или возникает ошибка времени компиляции. Оператор < может выполнять преобразования типов для своих операндов:

  • Если один из операндов имеет тип double, то другой операнд преобразуется в двойное.
  • В противном случае, если один из операндов имеет тип float, другой операнд преобразуется в число с плавающей запятой.
  • В противном случае, если один из операндов имеет тип long, другой операнд преобразуется в длинный.
  • В противном случае оба операнда преобразуются в int.

сравнение любых двух арифметических значений дает истинное если значение левого операнда меньше значения правого операнд; в противном случае сравнение дает ложь. Сравнение данных с плавающей запятой регулируется следующим дополнительные правила:

  • Если любой из операндов не является числом (NaN), сравнение дает false.
  • Отрицательный бесконечность — самое отрицательное значение. Если левый операнд отрицательный бесконечность, сравнение дает истину, если только правый операнд также равен отрицательной бесконечности, и в этом случае сравнение выдает ложное.
  • Положительная бесконечность является самым положительным значением. Если правый операнд положительная бесконечность, сравнение дает истину, если только левый операнд также положительная бесконечность, и в этом случае сравнение выдает ложное.
  • Положительный и отрицательный нули считаются равными, поэтому -0,0 < 0,0 выдает ложное.

Каталожные номера Арифметические типы

Оператор «меньше или равно»

<=

Оператор «меньше или равно» <= выполняет сравнение между своими операндами и возвращает логическое значение. Он возвращает чистое значение true, если его левый операнд меньше или равно своему правому операнду; в противном случае оператор возвращает чистое значение false. <= оператор может появляться как часть реляционного выражения. Оператор «меньше или равно» никогда не генерирует исключение.

типы обоих операндов оператора «меньше или равно» должны быть арифметическими типами, иначе произойдет ошибка времени компиляции. <= оператор может выполнять преобразования типов для своих операндов:

  • Если один из операндов имеет тип double, то другой операнд преобразуется в двойное.
  • В противном случае, если один из операндов имеет тип float, другой операнд преобразуется в число с плавающей запятой.
  • В противном случае, если один из операндов имеет тип long, другой операнд преобразуется в длинный.
  • В противном случае, оба операнда преобразуются в int.

сравнение любых двух арифметических значений дает истинное если значение левого операнда меньше или равно значению правого операнда; в противном случае сравнение дает ложь. Сравнение данных с плавающей запятой регулируется следующим дополнительные правила:

  • Если любой из операндов не является числом (NaN), сравнение дает false.
  • Отрицательный бесконечность — самое отрицательное значение. Если левый операнд отрицательный бесконечность, сравнение всегда дает истину.
  • Положительная бесконечность — самое положительное значение. Если право операнд положительная бесконечность, сравнение всегда дает истину.
  • Положительный и отрицательный нуль считаются равными, поэтому 0.0 <= -0.0 дает истину.

Каталожные номера Арифметические типы

Оператор «больше или равно» >=

Оператор больше или равно >= выполняет сравнение между своими операндами и возвращает логическое значение. Он возвращает чистое значение true, если его левый операнд больше или равно своему правому операнду; в противном случае оператор возвращает чистое значение false. >= оператор может появляться как часть реляционного выражения. Оператор «больше или равно» никогда не генерирует исключение.

Типы обоих операндов оператора больше или равно должны быть арифметическими типами, иначе произойдет ошибка времени компиляции. Оператор >= может выполнять преобразования типов на его операнды:

  • Если один из операндов имеет тип double, то другой операнд преобразуется в двойное.
  • В противном случае, если один из операндов имеет тип float, другой операнд преобразуется в число с плавающей запятой.
  • В противном случае, если один из операндов имеет тип long, другой операнд преобразуется в длинный.
  • В противном случае, оба операнда преобразуются в int.

сравнение любых двух арифметических значений дает истинное если значение левого операнда больше или равно значение правого операнда; в противном случае сравнение дает ложь. Сравнение данных с плавающей запятой регулируется следующим дополнительные правила:

  • Если любой из операндов не является числом (NaN), сравнение дает false.
  • Отрицательный бесконечность — самое отрицательное значение. Если правый операнд отрицательный бесконечность, сравнение всегда дает истину.
  • Положительная бесконечность — самое положительное значение. Если левый операнд положительная бесконечность, сравнение всегда дает истину.
  • Положительный и отрицательный ноль считаются равными, поэтому -0,0 >= 0. 0 дает истину.

Каталожные номера Арифметические типы

Оператор «больше чем» >

Оператор «больше» > выполняет сравнение между своими операндами и возвращает логическое значение ценность. Он возвращает чистое значение true, если его левый операнд больше правого операнда; в противном случае оператор возвращает чистое значение false. > оператор может появляться как часть реляционного выражения. Больше, чем оператор никогда не генерирует исключение.

Типы оба операнда оператора «больше» должны быть арифметическими типами, или возникает ошибка времени компиляции. Оператор > может выполнять преобразования типов для своих операндов:

  • Если один из операндов имеет тип double, то другой операнд преобразуется в двойное.
  • В противном случае, если один из операндов имеет тип float, другой операнд преобразуется в число с плавающей запятой.
  • В противном случае, если один из операндов имеет тип long, другой операнд преобразуется в длинный.
  • В противном случае оба операнда преобразуются в int.

сравнение любых двух арифметических значений дает истинное если значение левого операнда больше значения правый операнд; в противном случае сравнение дает ложь. Сравнение данных с плавающей запятой регулируется следующим дополнительные правила:

  • Если любой из операндов не является числом (NaN), сравнение дает false.
  • Отрицательный бесконечность — самое отрицательное значение. Если правый операнд отрицательный бесконечность, сравнение дает истину, если только левый операнд также равен отрицательной бесконечности, и в этом случае сравнение выдает ложное.
  • Положительная бесконечность является самым положительным значением. Если левый операнд равен положительной бесконечности, сравнение дает истину, если только правильное операнд также положительная бесконечность, и в этом случае сравнение выдает ложное.
  • Положительный и отрицательный нули рассматриваются как равные, поэтому 0,0 > -0,0 дает ЛОЖЬ.

Каталожные номера Арифметические типы

Экземпляр Оператора

Оператор instanceof выполняет сравнение типов между своими операндами и возвращает логическое значение. Возвращает чистое значение true, если объект на который ссылается левый операнд, можно привести к указанному типу как правый операнд; в противном случае оператор возвращает чистое значение ЛОЖЬ. Если значение левого операнда равно нулю, оператор instanceof возвращает чистое значение ЛОЖЬ. Оператор instanceof может появляться как часть реляционного выражения. Экземпляр оператор никогда не генерирует исключение.

Тип левый операнд экземпляра оператор должен быть ссылочным типом, иначе произойдет ошибка времени компиляции.

Все объекты наследуют метод equals(). из класса Объект. Равно () метод, определенный в классе Object, возвращает true если два сравниваемых объекта являются одним и тем же объектом. Для некоторых классов, более уместно переопределить метод equals() метод, чтобы он сравнивал содержимое двух объектов. До такой метод может выполнять сравнение, он должен проверять, что объекты являются экземплярами одного и того же класса с помощью instanceof. Например, предположим, что вы определяете класс для представления комплексные числа. Поскольку вы хотите equals() метод для сравнения содержимого комплексных числовых объектов, которые вы определяете метод equals для класса комплексных чисел это выглядит так:

логическое равно (объект о) {
   если (o экземпляр сложного числа)
       вернуть o.real == this.real
           && o.imaginary == this.imaginary;
}
 

Экземпляр оператор также может использоваться, чтобы узнать, является ли объект экземпляром класса, реализующего интерфейс. Например:

if (o instanceof Runnable)
    (новый поток ((Runnable) o). start;
 

Каталожные номера Отливки; типы классов; Типы интерфейсов

Операторы

8.1. Оператор Spread

Оператор Spread-dot ( *. ), часто сокращенно просто Оператор Spread, используется для вызова действия над всеми элементами. агрегатного объекта. Это эквивалентно вызову действия над каждым элементом и сбору результата в список:

 class Car {
    Строка сделать
    Струнная модель
}
деф автомобили = [
       новый автомобиль(марка: "Пежо", модель: "508"),
       новый автомобиль(марка: 'Renault', модель: 'Clio')]   (1) 
def makes = автомобили*.make   (2) 
assert makes == ['Peugeot', 'Renault']   (3)  
1 построить список из автомобилей элементов. Список представляет собой совокупность объектов.
2 вызвать оператора спреда из списка, обратившись к свойству make каждого элемента
3 возвращает список строк, соответствующих набору make элементов

Выражение cars*. make эквивалентно cars.collect{ it.make } . Нотация Groovy GPath позволяет сократить путь, когда указанное свойство не является свойством содержащего списка, в этом случае он автоматически распространять. В ранее упомянутом случае выражение cars.make может можно использовать, хотя часто рекомендуется сохранять явный оператор с расставленными точками.

Оператор распространения является нулевым, что означает, что если элемент коллекции имеет значение null, он вернет null вместо того, чтобы выдать NullPointerException :

 cars = [
   новый автомобиль(марка: "Пежо", модель: "508"),
   ноль,   (1) 
   новый автомобиль(марка: 'Renault', модель: 'Clio')]
assert cars*.make == ['Peugeot', null, 'Renault']   (2) 
утверждать null*.make == null   (3)  
1 построить список, для которого один из элементов нулевой
2 с использованием оператора распространения вызовет , а не исключение NullPointerException
3 получатель также может быть нулевым, и в этом случае возвращаемое значение равно ноль

Оператор распространения можно использовать в любом классе, реализующем интерфейс Iterable :

 class Component {
    Длинный идентификатор
    Имя строки
}
класс CompositeObject реализует Iterable {
    Компоненты защиты = [
        новый компонент (id: 1, имя: «Foo»),
        новый компонент (id: 2, имя: «Бар»)]

    @Override
    Итератор<Компонент> итератор() {
        компоненты. iterator()
    }
}
def составной = новый составной объект ()
утверждать составной*.id == [1,2]
утверждать составное*.имя == ['Foo','Bar'] 

Использовать несколько вызовов оператора растровой точки (здесь cars*.models*.name ), когда работа с агрегатами структур данных, которые сами содержат агрегаты:

 class Make {
    Имя строки
    Список моделей
}

@канонический
модель класса {
    Имя строки
}

деф автомобили = [
    новая марка (название: «Пежо»,
             модели: [новая модель('408'), новая модель('508')]),
    новый Марка (название: «Рено»,
             модели: [новая модель('Clio'), новая модель('Captur')])
]

def makes = автомобили*.название
assert делает == ['Peugeot', 'Renault']

def модели = автомобили*.модели*.название
утверждать модели == [['408', '508'], ['Clio', 'Captur']]
assert models.sum() == ['408', '508', 'Clio', 'Captur'] // выравнивание на один уровень
assert models.flatten() == ['408', '508', 'Clio', 'Captur'] // выравниваем все уровни (в данном случае один) 

Рассмотрите возможность использования метода collectNested DGM вместо оператора с расставленными точками для коллекций коллекций:

 class Car {
    Строка сделать
    Струнная модель
}
деф автомобили = [
   [
       новый автомобиль(марка: 'Peugeot', модель: '408'),
       новый автомобиль(марка: 'Peugeot', модель: '508')
   ], [
       новый автомобиль(марка: 'Renault', модель: 'Clio'),
       новый автомобиль(марка: 'Renault', модель: 'Captur')
   ]
]
def models = cars. collectNested{ it.model }
утверждать модели == [['408', '508'], ['Clio', 'Captur']] 
8.1.1. Распространение аргументов метода

Возможны ситуации, когда аргументы вызова метода можно найти в списке, который необходимо адаптировать к методу аргументы. В таких ситуациях вы можете использовать оператор распространения для вызова метода. Например, представьте, что у вас есть следующая подпись метода:

 int function (int x, int y, int z) {
    х*у+г
} 

тогда, если у вас есть следующий список:

 def args = [4,5,6] 

вы можете вызвать метод без определения промежуточных переменных:

 assert function(*args) == 26 

Можно даже смешивать обычные аргументы с расширенными:

 args = [4]
функция утверждения(*args,5,6) == 26 
8.1.2. Элементы списка распространения

При использовании внутри литерала списка оператор расширения действует так, как если бы содержимое элемента расширения было встроено в список:

 def items = [4,5]   (1) 
список по умолчанию = [1,2,3,*элементы,6]   (2) 
список утверждений == [1,2,3,4,5,6]   (3)  
1 элементов это список
2 мы хотим вставить содержимое списка элементов непосредственно в список без вызова addAll
3 содержимое элементов было встроено в список
8.
1.3. Элементы карты распространения

Оператор карты распространения работает аналогично оператору списка распространения, но для карт. Это позволяет вам встроить содержимое карты в другой литерал карты, как в следующем примере:

 def m1 = [c:3, d:4]   (1) 
карта защиты = [a:1, b:2, *:m1]   (2) 
утверждать карту == [a:1, b:2, c:3, d:4]   (3)  
1 m1 это карта, которую мы хотим встроить
2 мы используем нотацию *:m1 для распространения содержимого m1 на карту
3 карта содержит все элементы m1

Должность оператора карты спреда имеет значение, как показано в следующем примере:

 по умолчанию m1 = [c:3, d:4]   (1) 
карта защиты = [a:1, b:2, *:m1, d: 8]   (2) 
assert map == [a:1, b:2, c:3, d:8]   (3)  
1 m1 это карта, которую мы хотим встроить
2 мы используем нотацию *:m1 для распространения содержимого m1 на карту , но переопределить ключ d после распространяя
3 карта содержит все ожидаемые ключи, но d был переопределен

8.

2. Оператор диапазона

Groovy поддерживает концепцию диапазонов и предоставляет нотацию ( .. ) для создания диапазонов объектов:

 def range = 0..5   (1) 
утверждать (0..5).collect() == [0, 1, 2, 3, 4, 5]   (2) 
утверждать (0..<5).collect() == [0, 1, 2, 3, 4]   (3) 
утверждать (0<..5).collect() == [1, 2, 3, 4, 5]   (4) 
утверждать (0<..<5).collect() == [1, 2, 3, 4]   (5) 
утверждать (0..5) instanceof List   (6) 
утверждать (0..5).size() == 6   (7)  
1 простой диапазон целых чисел, сохраненный в локальной переменной
2 и IntRange с включенными границами
3 и IntRange с исключительной верхней границей
4 и IntRange с эксклюзивной нижней границей
5 и IntRange с исключительными нижними и верхними границами
6 a groovy. lang.Range реализует интерфейс List
7 означает, что вы можете вызвать на нем метод size

Реализация диапазонов упрощена, что означает, что сохраняются только нижняя и верхняя границы. Вы можете создать диапазон из любых Сопоставимый объект , который имеет методы next() и previous() для определения следующего/предыдущего элемента в диапазоне. Например, вы можете создать диапазон символов следующим образом:

 assert('a'..'d').collect() == ['a','b','c','d'] 

8.3. Оператор космического корабля

Оператор космического корабля ( <=> ) делегирует метод compareTo :

 assert (1 <=> 1) == 0
утверждать (1 <=> 2) == -1
утверждать (2 <=> 1) == 1
утверждать ('a' <=> 'z') == -1 

8.4. Оператор нижнего индекса

Оператор нижнего индекса представляет собой сокращенную запись для getAt или putAt , в зависимости от того, найдете ли вы его на левая или правая часть задания:

 def list = [0,1,2,3,4]
список утверждений[2] == 2   (1) 
список[2] = 4   (2) 
утвердить список[0. .2] == [0,1,4]   (3) 
список[0..2] = [6,6,6]   (4) 
список утверждений == [6,6,6,3,4]   (5)  
1 [2] можно использовать вместо getAt(2)
2 , если слева от задания, вызовет putAt
3 getAt также поддерживает диапазоны
4 так же как и putAt
5 список изменен

Оператор нижнего индекса в сочетании с пользовательской реализацией getAt / putAt является удобным способом деструктурирования объекты:

 класс пользователя {
    Длинный идентификатор
    Имя строки
    защита getAt(int i) {   (1) 
        переключатель (я) {
            случай 0: возвращаемый идентификатор
            случай 1: вернуть имя
        }
        выбросить новое исключение IllegalArgumentException("Нет такого элемента $i")
    }
    void putAt (int i, значение по умолчанию) {   (2) 
        переключатель (я) {
            случай 0: идентификатор = значение; возвращаться
            случай 1: имя = значение; возвращаться
        }
        выбросить новое исключение IllegalArgumentException("Нет такого элемента $i")
    }
}
def user = новый пользователь (id: 1, имя: «Алекс»)   (3) 
утвердить пользователя [0] == 1   (4) 
утвердить пользователя [1] == 'Алекс'   (5) 
пользователь [1] = 'Боб'   (6) 
утверждать user. name == 'Боб'   (7)  
1 класс User определяет пользовательский getAt реализация
2 класс User определяет пользовательскую реализацию putAt
3 создать пробного пользователя
4 с помощью оператора индекса с индексом 0 позволяет получить идентификатор пользователя
5 с помощью оператора индекса с индексом 1 позволяет получить имя пользователя
6 мы можем использовать оператор нижнего индекса для записи свойства благодаря делегированию putAt
7 и проверьте, действительно ли это свойство имя было изменено

8.

5. Оператор безопасного индексирования

В Groovy 3.0.0 представлен оператор безопасного индексирования, т. е. 9.0313 ?[] , что похоже на ?. . Например:

 Строка[] массив = ['a', 'b']
assert 'b' == array?[1] // получаем с использованием индекса обычного массива
array?[1] = 'c' // устанавливаем с использованием индекса обычного массива
утверждать 'c' == массив? [1]

массив = ноль
assert null == array?[1] // возвращаем null для всех значений индекса
array?[1] = 'c' // тихо игнорируем попытку установить значение
утверждать массив null ==?[1]

def personInfo = [имя: 'Daniel.Sun', местоположение: 'Шанхай']
assert 'Daniel.Sun' == personInfo?['name'] // используем индекс карты нормалей
personInfo?['name'] = 'sunlan' // устанавливается с использованием индекса карты нормалей
утверждать 'sunlan' == personInfo?['name']

информация о человеке = ноль
assert null == personInfo?['name'] // возвращаем null для всех значений карты
personInfo?['name'] = 'sunlan' // незаметно игнорируем попытку установить значение
утверждать null == personInfo?['name'] 

8.

6. Оператор принадлежности

Оператор принадлежности ( в ) эквивалентен вызову метода isCase . В контексте списка это эквивалентно для вызова содержит , как в следующем примере:

 def list = ['Grace','Rob','Emmy']
утверждать («Эмми» в списке)   (1)  
1 эквивалентно вызову list.contains('Эмми') или list.isCase('Эмми')

8.7. Оператор идентификации

В Groovy использование == для проверки равенства отличается от использования того же оператора в Java. В Groovy вызов равен . Если вы хотите сравнить ссылочное равенство, вы должны использовать равно , как в следующем примере:

 def list1 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']   (1) 
def list2 = ['Groovy 1. 8','Groovy 2.0','Groovy 2.3']   (2) 
утверждать список1 == список2   (3) 
утверждать !list1.is(list2)   (4)  
1 Создать список строк
2 Создать другой список строк, содержащих те же элементы
3 используя == , мы проверяем равенство объектов
4 , но использование равно , мы можем проверить, что ссылки различны

8.8. Оператор приведения

Оператор приведения ( as ) является вариантом приведения. Приведение преобразует объект из одного типа в другой без их быть совместимым для назначения. Возьмем пример:

 Целое число x = 123
Строка s = (Строка) x   (1)  
1 Целое число не может быть назначено String , поэтому оно создаст ClassCastException во время выполнения

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

 Целое число x = 123
Строка s = x as Строка   (1)  
1 Целое число не может быть присвоено строке , но использование в качестве приведет к принудительному преобразованию в строку

Когда объект принудительно преобразуется в другой, если целевой тип не совпадает с исходным типом, приведение возвращает новый объект . Правила приведения различаются в зависимости от исходного и целевого типов, и приведение может завершиться ошибкой, если не выполняется преобразование. правила найдены. Пользовательские правила преобразования могут быть реализованы благодаря asType метод:

 класс Идентифицируемый {
    Имя строки
}
класс Пользователь {
    Длинный идентификатор
    Имя строки
    def asType (Цель класса) {   (1) 
        если (цель == Идентифицируемый) {
            вернуть новый идентифицируемый (имя: имя)
        }
        выбросить новое исключение ClassCastException («Пользователь не может быть принужден к $ target»)
    }
}
def u = новый пользователь (имя: «Ксавье»)   (2) 
def p = u как идентифицируемый   (3) 
утверждать p instanceof Идентифицируемый   (4) 
assert !(p instanceof User)   (5)  
1 класс User определяет пользовательское правило преобразования с User на Идентифицируемый
2 мы создаем экземпляр пользователя
3 мы принуждаем экземпляр пользователя к идентифицируемому
4 цель является экземпляром Идентифицируемый
5 цель не является экземпляром Пользователь Больше

8.

9. Алмазный оператор

Алмазный оператор ( <> ) является синтаксическим сахарным оператором, добавленным для поддержки совместимости с оператором то же имя в Java 7. Оно используется для указания того, что универсальные типы должны выводиться из объявления:

 List strings = new LinkedList<>() 

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

8.10. Оператор вызова

Оператор вызова () используется для неявного вызова метода с именем call . Для любого объекта, который определяет метод вызова , вы можете опустить часть .call и вместо этого использовать оператор вызова:

 class MyCallable {
 внутренний вызов (int x) {   (1)  2*х
 }
}  def mc = новый MyCallable()
утверждать mc.