© 2008 Наталия Македа
Все материалы блога защищены авторским правом. Любая перепечатка или использование материалов этого блога в коммерческих целях возможна лишь с письменного согласия автора. При некоммерческом использовании ссылка на блог обязательна.

среда, 28 мая 2008 г.

2.5 Операторы сравнения (Выпуск 8)

Операторы сравнения, <, >, <=, >=, ==, !=, возвращают результат типа boolean, то есть true или false. Эти операторы обычно используются в условных конструкциях (например, if () или циклы). Существует три типа сравнения:

  1. порядковое (ordinal) тестирует относительные значения числовых операндов.
  2. объектно-ориентированное определяет тип объекта во время исполнения программы.
  3. операторы равенства тестируют, одинаковы ли два значения, в том числе и нечисленные.

Рассмотрим их все подробнее.

Порядковые операторы (<, >, <=, >=)

К порядковым операторам относятся < (меньше), > (больше), <= (меньше либо равно), >= (больше либо равно). Они применяются ко всем численным типам и типу char и возвращают результат boolean. Например, если у нас имеются следующие объявления:

int p = 9;
int q = 65;
int r = -12;
float f = 9.0F;
char c = ‘A’;

то следующие тесты возвратят true:

p < q
f < q
f <= c
c > r
c >= q

Заметьте, что, когда эти операторы используются, применяется арифметическое распространение. Например, будет ошибкой присвоение значение 9.0F типа float переменной c типа char. Но к этой паре может быть применено сравнение! Для этого Java распространяет меньший тип к большему типу. То есть значение 'A' типа char (представляемое значением 65 в Unicode) распространяется до float 65.0F. Сравнение затем выполняется на результирующей паре значений float.

Порядковые сравнения не могут быть применены к объектным типам!

Оператор instanceof

Оператор instanceof тестирует класс объекта в момент исполнения программы (runtime). Левый операнд - любое выражение объектного типа (переменная или массив). Правый операнд - имя класса, интерфейса или массивный тип. Однако, левым операндом не может быть объект типа java.lang.Class, а правым операндом не может быть String.

Фрагмент кода ниже показывает пример использования оператора instanceof. Предположим, что существует класс Person с подклассом Parent:

1. public class Classroom {
2. private Hashtable inTheRoom = new Hashtable();
3. public void enterRoom(Person p) {
4. inTheRoom.put(p.getName(), p);
5. }
6. public Person getParent(String name) {
7. Object p = inTheRoom.get(name);
8. if (p instanceof Parent) {
9. return (Parent)p;
10. }
11. else {
12. return null;
13. }
14. }
15. }

Метод getParent() в строках 6-14 проверяет, содержит ли Hashtable родителя со специфическим именем. Для этого в Hashtable сначал ищется элемент со специфическим именем родителя. А потом проверяется, является ли этот элемент объектом типа Parent. Оператор instanceof проверяет, совпадает ли класс левого операнда с названием класса, заданного правым операндом или является его подклассом.

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

Опертор также используется для тестирования, является ли объект массивом. Поскольку массивы в Java являются объектами, то такое тестирование логично. Но тест проводится в два шага: (1) является ли объект массивом и (2) является ли тип элементов массива (под)классом правого аргумента. Это отражает идею, что массив, скажем, объектов типа Button (кнопки) является массивом объектов типа Component (компоненты), поскольку Button - это Component. Тест для такого массива будет выглядеть так:

if (x instanceof Component[])

Заметьте, что невозможно провести тестирование для "любого массива с элементами любого типа". То есть следующая строка недопустима:

if (x instanceof [])

Недопустимо также тестирование на массив с элементами типа Object:

if (x instanceof Object [])

поскольку элементы массива могут быть примитивного типа и тест, попросту, не сработает.

А как же протестировать, является ли объект массивом без привязки к типу его элементов? Допустим myObject это массив. Тогда следующая строка возвратит true:

myObject.getClass().isArray()

Если левый операнд равен null, то instanceof возвратит false. Никокого исключения не возникнет.

Операторы равенства (== и !=)

Операторы == и != тестируют, соответственно, равенство или неравенство. Для примитивных типов концепция равенства или неравенства достаточно тривиальна. Как и в случае с операторами порядка, операнды распространяются до наибольшего. Например, значение 10.0 типа float равно значению 10 типа byte. Для значений объектного типа, сравниваемая величина - это ссылка на объект, то есть адрес памяти.

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

Метод equals() должен был определён в классе сравниваемых объектов. Чтобы быть уверенными в этом, проверьте документацию. В документации должно быть указано, что equals() определён в классе или переопределяет (overrides) метод equals() своего супер-класса. Если документация ничего по этому поводу не говорит, то метод equals() будет, скорее всего, работать неправильно. Метод equals() принимает аргумент типа Object. Но в реальности вы должны передавать ему аргумент того же типа, что и объект, для которого он вызывается. Например, если вы вызываете x.equals(y), то y instanceof x должно возвращать true. Если же это не так, то equals() вернёт false.
Если вы определяете equals() в вашем собственном классе, то вы должны соблюдать три правила:
  • Аргумент для equals() должен быть объект типа Object. Не поддавайтесь искушению вставить аргумент того же типа, что и класс. Иначе вы перегрузите (overload) метод, а не переопределите (override) его. В итоге функциональность других частей кода, зависящих от equals() будет неправильна. Например, итерации по HashMap, то есть containsKey() и get(), будут неправильны.
  • equals() должен быть коммутативным оператором. То есть результат x.equals(y) должен быть равен результату y.equals(x).
  • Если вы лпределяете equals() в своём классе, то вы должны определить и hashCode(), который возвращает одно и то же значение для объектов, сравниваемых методом equals(). Это, опять таки, нужно для правильного функционирования итераций по контейнерам. Минимально допустимое поведение hashCode() - это return 1. Конечно, при такой имплементации потеряются все преимущества "настоящего" хеширования и HashMap будет просто связным списком. Но, по крайней мере, функциональность будет корректной.

Комментариев нет: