Как работать с объектами: 2‑я часть гайда по ООП
Только работа с объектами начинает казаться простой, как тут же появляются новые сюрпризы. Рассказываем, как их избежать.
vlada_maestro / shutterstock
Это вторая статья из цикла про ООП. Поэтому советуем сначала прочесть предыдущую часть.
Многие новички сталкиваются с тем, что объекты ведут себя не так, как планировалось, хотя всё вроде бы написано правильно. Кроме того, такой код только что корректно работал с обычными переменными.
На самом деле никакой ошибки, скорее всего, нет. А все дело в том, что работа с объектами отличается от работы с обычными переменными. Разбираемся, почему так происходит.
Все статьи про ООП
Работа со ссылочным типом данных
Данные, с которыми работает программа, хранятся в оперативной памяти. Для этого используются ячейки, у которых есть адрес (ключ) и значение.
Раньше программистам приходилось запоминать эти ключи или записывать их в отдельный блокнот. Но современные языки программирования позволяют создавать переменные (их называют примитивными или значимыми типами данных), у которых вместо адреса идентификатор:
Для каждой переменной выделяется одна ячейка памяти. Однако объекты гораздо больше обычной переменной, потому что в них может быть сразу несколько полей. Можно создать переменную hero, в которой будет объект класса Character:
Но в ней будет храниться не сам объект, а ссылка на него. Поэтому объекты относятся к ссылочным типам данных, и из-за этого работа с ними отличается от работы с примитивными типами.
Рассмотрим такой код:
Здесь сначала создаются, а потом сравниваются два идентичных объекта. Если они равны, то выводится true, а иначе — false. Вот что выведет программа:
Несмотря на то что все поля совпадают, программа выводит false. И это не баг. Дело в том, что сравниваются не значения полей объектов, а ссылки на них. А поскольку это два различных объекта, то и адреса у них разные.
Строка true будет выведена только в том случае, если в обеих переменных находится ссылка на один и тот же объект. Чтобы это проверить, в коде из примера выше нужно изменить значение переменной hero1:
Теперь программа выведет true, потому что это один и тот же объект:
И теперь, если изменить hero1, hero тоже изменится:
Тут меняются координаты объекта hero1 с помощью метода Move (), а затем выводятся координаты объекта hero. Они были [50, 20], но теперь по оси Y значение будет 21:
Если нужно проверить, совпадают ли значения полей у объектов, то надо вручную проверять каждое поле:
Однако это неудобно, если полей очень много. Чтобы упростить эту задачу, можно использовать метод Equals () — его мы рассмотрим в статье про полиморфизм. Если же надо сделать копию, то можно написать метод Clone () или Copy (). Он будет принимать объект, чтобы скопировать его данные:
Или же можно написать конструктор, который будет принимать в качестве аргумента другой объект.
Использование объекта в качестве аргумента
Сразу же стоит отметить, что у передачи объекта в качестве аргумента есть побочные эффекты. Рассмотрим такой код:
В методе Multiply () полученное число умножится на два. Но так как int — это значимый тип, то в метод будет передана не ссылка на это значение в памяти, а само число. То есть будет создана новая переменная (их называют локальными) с таким же значением, и уже она будет умножена на два. На переданное значение это никак не повлияет.
Вот что выведет программа:
Однако если использовать объект, то будет передана ссылка на него, а все операции будут проводиться с ним, а не с его значением:
Вот что выведет программа:
Такие особенности ссылочных типов нужно учитывать при разработке программ, чтобы не происходило ничего неожиданного. Например, когда значение меняется, хотя и не должно.
Домашнее задание
Создайте класс Game, в котором будут храниться все игровые объекты. Дайте персонажу возможность перемещаться по карте и взаимодействовать с объектами, координаты которых совпадают с его позицией.
Подсказка
Используйте коллекции и циклы.
Заключение
Практикуйтесь и работайте с объектами как можно чаще, и тогда вы сможете овладеть ими. Однако, чтобы писать код без костылей, нужно понимать, как он работает, — в том числе это касается и ссылочных, и значимых типов данных. Кроме того, понимание различий между этими типами избавит вас от неприятных сюрпризов.