Как сравнить названия классов в java
Перейти к содержимому

Как сравнить названия классов в java

  • автор:

Сравнение типов классов в Java

Я хотел сравнить в случае, если obj, переданный в функцию, был расширен из MyObject_1 или нет. Но это не работает. Похоже, что метод getClass() и .class предоставляет различный тип информации.

Как я могу сравнить два типа класса, не создавая другого фиктивного объекта, чтобы сравнить тип класса?

6 ответов

Из-за наследования это также верно для интерфейсов:

Если вы не хотите или не можете использовать instanceof , сравните с equals :

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

  • классы имеют одно и то же короткое имя, но определяются в разных пакетах
  • классы имеют одинаковое полное имя, но загружаются разными загрузчиками классов.

Он печатает true на моей машине. И это должно быть, иначе ничего в Java не будет работать так, как ожидалось. (Это объясняется в JLS: 4.3.4 Когда ссылочные типы одинаковы)

Есть ли у вас несколько загрузчиков классов?

Ah, и в ответ на этот комментарий:

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

MyImplementedObject реализует MyObjectInterface Итак, другими словами, я сравниваю его с его реализованным объекты.

ОК, если вы хотите проверить, что вы можете сделать это:

или гораздо более кратким

Как уже говорилось ранее, ваш код будет работать, если у вас нет одинаковых классов, загруженных на двух разных загрузчиках классов. Это может произойти в случае, если вам потребуется несколько версий одного и того же класса в памяти одновременно, или вы делаете какие-то странные вещи для компиляции на лету (как и я).

В этом случае, если вы хотите рассматривать их как один и тот же класс (что может быть разумным в зависимости от случая), вы можете сопоставить их имена, чтобы сравнить их.

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

Сравнение объектов в Java

Сравнение объектов является важной функцией объектно-ориентированных языков программирования. В этом руководстве мы рассмотрим некоторые функции языка Java, которые позволяют нам сравнивать объекты. Также мы обратим внимание на подобные функции во внешних библиотеках.

Операторы == и !=

Давайте начем с операторов == и != , которые могут сказать, одинаковы ли Java-объекты или нет соответственно.

Примитивы

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

Благодаря автоматической распаковке это также работает при сравнении примитивного значения с его классом-оберткой:

Если две целочисленные переменные имеют разные значения, оператор == вернет false , а оператор != вернет true .

Объекты

Предположим, мы хотим сравнить два экземпляра классов-оберток Integer с одинаковыми значениями:

При сравнении двух объектов их значения не равняются 1. Скорее различаются их адреса памяти в стеке, поскольку оба объекта создаются с использованием оператора new . Если мы присвоим переменной а переменную b , то получим другой результат:

Теперь давайте посмотрим, что происходит, когда мы используем фабричный метод Integer#valueOf :

В этом случае они считаются одинаковыми. Это связано с тем, что метод valueOf() держит Integer в кеше, чтобы избежать создания слишком большого количества оберток с одинаковыми значениями. Поэтому метод возвращает один и тот же экземпляр Integer для обоих вызовов.

Java также делает это для переменных типа String:

Однако, если они созданы с помощью оператора new , они не будут одинаковыми.

Наконец, две нулевые ссылки считаются одинаковыми, в то время как любой не-null объект считается отличным от null:

Конечно, поведение операторов равенства может быть ограничивающим. Что если мы хотим сравнить два объекта, расположенных по разным адресам и при этом полагать, что они равны на основании их внутренних состояний? Как это сделать, мы увидим в следующих пунктах.

Метод Object#equals

Теперь давайте поговорим о более широком концепте равенства с методом equals() .

Этот метод определен в классе Object , поэтому каждый объект Java наследует его. По умолчанию его реализация сравнивает адреса памяти объектов, поэтому он работает так же, как и оператор == . Однако мы можем переопределить этот метод, чтобы определить, что для наших объектов означает равенство внутренних состояний.

Во-первых, давайте посмотрим, как он ведет себя для существующих объектов, таких как Integer:

Наш метод по-прежнему возвращает true, когда оба объекта одинаковы.

Отметим здесь, что мы можем передать объект null в качестве аргумента метода, но не в качестве объекта, для которого мы вызываем метод.

Мы также можем использовать метод equals() с любым пользовательским объектом. Допустим, у нас есть класс Person:

Мы можем переопределить метод equals() для этого класса, чтобы мы могли сравнить два объекта Persons на основе их внутренних данных:

Чтобы узнать больше по этой теме, читайте вот эту статью.

Статический метод Objects#equals

Давайте посмотрим на статический метод Objects#equals . Ранее мы упоминали, что нельзя использовать null в качестве значения первого объекта, иначе будет выброшено исключение NullPointerException .

Метод equals() вспомогательного класса Objects решает эту проблему. Он принимает два аргумента и сравнивает их, а также обрабатывает значения null.

Давайте снова сравним объекты Person :

Как мы объяснили, этот метод обрабатывает значения null. Следовательно, если оба аргумента равны null, он вернет значение true, а если только один из них равен null, он вернет значение false.

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

Затем нужно обновить метод equals() , но с обработкой null значений. Мы можем сделать это, добавив условие в метод equals() :

Однако же, если мы добавим слишком много полей с возможными null значениями в класс, он может стать запутанным. Использование метода Objects#equals в реализации equals() намного чище, что улучшает читаемость кода.

Интерфейс Comparable

Логику сравнения также можно использовать для размещения объектов в определенном порядке. Интерфейс Comparable позволяет нам определять порядок между объектами, выявляя, является ли объект больше, меньше или равным другому.

Интерфейс Comparable является дженериком и имеет только один метод, compareTo() , который принимает аргумент дженерик-типа и возвращает int. Возвращаемое значение отрицательное, если this меньше аргумента, 0, в случае если они равны, и положительное в обратном случае.

Допустим, в классе Person мы хотим сравнить объекты Person по их фамилии:

Метод compareTo() вернет отрицательный int, если оно вызвано с именем Person , имеющим большую фамилию, чем this, ноль, если ту же фамилию, и положительное значение в обратном случае.

Чтобы узнать подробнее, прочитайте статью на эту тему.

Интерфейс Comparator

Интерфейс Comparator является дженериком и содержит метод compare , который принимает два аргумента этого типа и возвращает integer. Мы ранее уже видели этот шаблон с интерфейсом Comparable.

Comparator аналогичен ему; однако он отделен от определения класс. Следовательно, мы можем определить столько Comparator , сколько захотим для одного класса, где мы можем предоставить только одну реализацию Comparable .

Представим, что у нас есть веб-страница, содержащая список людей в виде таблицы, и мы хотим предложить пользователю возможность сортировать их по именам, а не по фамилиям. Это невозможно с Comparable , если мы также хотим сохранить нашу текущую реализацию, но мы можем реализовать наши собственные Comparators .

Давайте создадим Person Comparator , который будет сравнивать их только по именам:

Теперь отсортируем список людей, используя Comparator :

В интерфейсе Comparator есть и другие методы, которые мы можем использовать в реализации compareTo() :

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

Apache Commons

Давайте взглянем на библиотеку Apache Commons. Прежде всего, импортируем зависимость Maven.

Метод ObjectUtils#notEqual

Для начала поговорим о методе ObjectUtils#notEqual . Потребуется два аргумента Object , чтобы определить, не равны ли они, в соответствии с их собственной реализацией метода equals() . Он также обрабатывает значения null.

Используем наши примеры со String повторно:

Следует отметить, что у ObjectUtils есть метод equals() , что однако является устаревшим с момента возникновения Java 7, когда появились Objects#equals .

Метод ObjectUtils#compare

Теперь давайте сравним порядок объектов с помощью метода ObjectUtils#compare . Это дженерик-метод, который принимает два аргумента Comparable дженерик типа и возвращает Integer.

Приведем пример, снова используя Strings:

По умолчанию этот метод обрабатывает значения null, считая их бОльшими. Он также предлагает перегруженную версию с аргументом boolean, которая предлагает изменить это поведение и считать их мЕньшими, принимая аргумент boolean.

Guava

Давайте взглянем на Guava. Прежде всего импортируем зависимость:

Метод Objects#equal

Как и библиотека Apache Commons, Google предоставляет нам метод определения равенства двух объектов, Objects#equal . Хотя у них разные реализации, возвращают они одинаковые результаты:

Хотя он не помечен как устаревший, в JavaDoc говорится, что этот метод следует считать устаревшим, поскольку Java 7 предоставляет метод Objects#equals .

Методы сравнения

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

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

Класс ComparisonChain

Наконец, библиотека Guava предлагает класс ComparisonChain , который позволяет нам сравнивать два объекта по цепочке сравнений. Мы можем легко сравнить два объекта Person по имени и фамилии:

Базовое сравнение достигается с помощью метода compareTo() , поэтому аргументы, передаваемые методам compare() , должны быть либо примитивными, либо Comparable.

Заключение

В этой статье мы узнали о разных способах сравнения объектов в Java. Мы изучили разницу между тождественностью, равенством и порядком. Мы также рассмотрели соответствующие функции в библиотеках Apache Commons и Guava.

Полный код из статьи можно найти на GitHub.

Приглашаем всех желающих на открытое занятие «Языки статической и динамической типизации». На этом вебинаре поговорим о стилях программирования и необходимости каждого из них. Разберём основные принципы объектно-ориентированного стиля (Инкапсуляция, Наследование, Полиморфизм). Рассмотрим возможности функционального стиля, которые предоставляет язык Java. Регистрация на вебинар.

Как сравнить названия классов в java

Равенство и сравнение в Java: подводные камни и лучшие практики

В Java есть разные методы сравнения объектов и примитивов, каждый со своей семантикой. Использование «неправильного» может привести к неожиданным результатам и может привести к появлению незаметных, трудно обнаруживаемых ошибок.

Прежде чем мы сможем узнать о подводных камнях и лучших практиках равенства и сравнения в Java, нам необходимо понять различные типы типов и их поведение.

Примитивы против объектов

Система типов Java состоит из восьми примитивных типов данных ( boolean , byte , char , short , int , long , float , double ) и ссылочных типов на объекты.

Примитивы

Примитивы в Java нельзя неинициализировать или null , они всегда имеют значение по умолчанию. Он представляет 0 , подходящий для конкретного типа данных:

Примитивные классы-оболочки

Каждый примитивный тип данных имеет соответствующий класс-оболочку в java.lang , инкапсулирующий его значение в объект Java:

Быть объектами позволяет использовать их в более широком диапазоне сценариев:

  • Универсальные типы (например, List<Integer> ).
  • Передача по ссылке, а не по значению.
  • Возможность быть нулевым.
  • и т.п.

Но нам также приходится сталкиваться со всеми недостатками. Как и NullPointerException , больший объем памяти и снижение производительности.

Автобокс и распаковка

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

Несмотря на то, что примитивы и ссылки на объекты имеют разную семантику, они могут использоваться взаимозаменяемо благодаря компилятору Java.

Autoboxing — это автоматическое преобразование примитивных типов в соответствующий класс оболочки, а unboxing — другое направление. Это позволяет нам использовать оба типа без дискриминации:

Наш List использует тип оболочки Integer , но наш код компилируется, хотя мы добавляем int . Это возможно благодаря тому, что компилятор изменил наш код, автоматически запаковав i :

То же верно и в обратном случае:

Несмотря на то, что мы используем такие операторы, как % и + , которые недоступны для типа объекта Integer , код компилируется нормально. Поскольку компилятор распаковывает тип оболочки. Фактический скомпилированный код выглядит примерно так:

Равенство

Если мы посмотрим на другие языки программирования, наиболее логичным выводом о том, как сравнивать значения, может быть оператор == и его антагонист != .

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

Примитивы

Примитивы — это литералы, фиксированные значения в памяти, которые можно проверить на равенство с == .

За исключением тех случаев, когда они не могут.

В отличие от других примитивных типов данных, типы данных с плавающей запятой float и double нельзя надежно проверить на равенство с == из-за их метода хранения в памяти. Это не точные значения:

У нас есть два варианта справиться с этим. Либо с помощью java.util.BigDecimal , что точно. Или используя сравнения на основе пороговых значений:

Массивы

Еще одна ловушка — это примитивные массивы, потому что массивы не являются примитивным типом, это объекты.

Объекты

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

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

На практике в одних случаях переменные могут быть равны, в других — нет:

Компилятор и JVM могут оптимизировать строковые константы, поэтому result2 равно true . И result3 это false , потому что a + b создает новый объект в памяти. Все это может зависеть от реализации и различаться для разных JVM.

Еще одна «не столь очевидная» ошибка может случиться с примитивными типами-оболочками:

Какие? Это тоже застало меня врасплох.

valueOf(. ) методы java.util.Integer и java.util.Long фактически кэшируют значения для определенных диапазонов (от -128 до 127), делая a и b одним и тем же объектом, но не c и d . А благодаря распаковке equalAgain это true .

Object.equals (Другой объект) и Object hashCode ()

Класс java.lang.Object предоставляет метод equals для всех своих подклассов с довольно простой реализацией:

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

  • Рефлексивно: объект должен быть равен самому себе: obj.equals(obj) == true .
  • Симметричный: если a.equals(b) == true , то b.equals(a) также должен быть true .
  • Переходный: если a.equals(b) == true и b.equals(c) == true , тогда a.equals(c) должно быть true .
  • Согласовано: a.equals(b) всегда должно иметь одно и то же значение для неизмененных объектов.
  • Обработка нулей: a.equals(null) должно быть false .
  • Хеш-код: одинаковые объекты должны иметь одинаковый хеш-код.

Если мы предоставляем наш собственный equals метод, нам также необходимо переопределить hashCode .

Начиная с Java 7, класс java.util.Objects предоставляет помощники для упрощения нашего кода:

Помните о сравнении классов в строке 19. Мы могли бы быть склонны использовать instanceof для сравнения объектов, но это может нарушить общий договор между equals и hashCode : одинаковые объекты должны иметь одинаковый хэш-код.

Конечно, мы можем спроектировать наши объекты так, чтобы даже подклассы были равны своим родителям. Но определение равенства должно быть одинаковым для обоих, вычисление хэш-кода должно происходить в базовом классе.

Таким образом определяются классы java.util.Date и его подкласс java.sql.Date . Версия sql не имеет методов equal или hashCode , и базовый класс строит свой хэш-код исключительно на основе отметки времени.

Другой пример — классы коллекций: java.util.ArrayList и java.util.LinkedList являются подклассами java.util.AbstractList и используют его методы equal и hashCode . Равенство для коллекций в большинстве случаев определяется равенством их содержимого, поэтому использование instanceof , а не жесткую проверку класса кажется уместным.

Сравнение

Просто проверки на равенство бывает достаточно редко. Другой важный вид операций — это сравнение значений.

Примитивы

Как и в других языках, мы можем сравнивать значения примитивов с < , > , <= и >= оператором.

К ним применимы те же проблемы, что и с типами данных с плавающей запятой, так что имейте в виду. Кроме того, boolean нельзя сравнивать, за исключением равенства с == и != .

java.lang.Comparable ‹T›

Объекты не поддерживают эти операторы. Для сравнения типов объектов нам необходимо реализовать интерфейс java.lang.Comparable<T> с его единственным методом int compareTo(T) .

Результат left.compareTo(right) должен быть следующим:

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

Лучшие практики

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

Никогда не сравнивайте объекты с ==

Это работает, только если это один и тот же объект. Различные объекты с одинаковым значением не равны. Всегда используйте boolean equals(Object other) для сравнения на равенство.

Всегда применяйте equals и hashCode if needed

Чтобы сделать тип проверяемым на равенство, нам необходимо реализовать как equals , так и hashCode , чтобы гарантировать правильное и последовательное поведение.

Типы данных с плавающей запятой неточны

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

Если нам нужно работать с десятичными значениями и нужна абсолютная точность, мы всегда должны использовать java.util.BigDecimal . Но имейте в виду, что его equals основан на его точности:

Если нам нужно более простое сравнение, мы можем использовать compareTo :

BigDecimal API не очень хорош по сравнению с примитивными операциями, в первую очередь из-за его неизменности. Но на самом деле это хорошо, ведь правильность важнее красоты.

Помните об автобоксе / распаковке

Поскольку компилятор делает это за кулисами, мы должны обязательно сравнивать примитивы или объекты-оболочки благодаря _111 _ / _ 112_ кэшированию.

Чтобы быть на 100% уверенным, мы могли бы использовать вместо этого Comparable<T>#compareTo(T) типов оболочки, которая всегда использует инкапсулированное значение, а не ссылку на объект.

Pro Java

Так как мы только начали изучать ООП, то эта статья может показаться сложной, но прочитать ее все же стоит, чтобы в сознании отложилось понимание что из себя представляют ссылочные типы данных. Мы уже немного говорили об этом во введении в типы данных Java.

Сейчас уже следует узнать, что при сравнении переменных с использованием оператора сравнения (==) примитивные типы сравниваются по значению, а ссылочные по ссылке . Сейчас эта фраза может быть не особо понятной, но надеюсь эта статья прояснит ситуацию.

Значения переменных примитивных типов хранятся в стеке, и каждая переменная примитивного типа хранит свое независимое значение в стеке. Со ссылочными типами вообще, и с классами обертками примитивов, в частности, все не так. В стеке хранится только ссылка на объект. Сам же объект храниться в так называемой куче (heap).

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

Также нужно обратить внимание, что JVM использует больше памяти, чем занимает куча. Например, для методов Java и стеков потоков выделяется память отдельно от кучи.

Размер кучи зависит от используемой платформы, но, как правило, это где-то между 2 и 128 Кб.

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

Ну для начала хватит уже теории и немного попрактикуемся, чтобы понять о чем идет речь.

Сперва рассмотрим простой пример и его вывод:

CW002

CW003

Здесь стоит обратить внимание на область выделенную желтым цветом. Все вроде как обычно и как должно быть. То есть все так как мы привыкли уже при работе с примитивными типами данных.

Ни что, вроде бы, не предвещает беды �� но надо быть на чеку. Грабли по всюду.

Программер, бди! И не бзди! ��

Теперь немного изменим программу и посмотрим что будет ��

CW004

CW005

Grabli

Отани то! Грабельки наши. Ну куда ж без них то родимых!

Надеюсь вы заметили в чем разница?

Теперь intA и intB хотя и равны 500, но между собой не равны �� Верней сказать они ссылаются на объекты которые содержат значение поля int равное 500.

Так в чем же дело? Давайте разбираться. Во первых надо отметить что intA и intB это ссылки на объекты типа Integer. Но почему же тогда при значении 50 они были равны, а при значении 500 они вдруг стали не равны? Все дело в кэше. Кэш в данном случае это не деньги �� Это массив для кэширования значений, который находится во внутреннем классе класса Integer. Так как мы классы, а тем более внутренние классы, еще не проходили то это все, скорей всего, вам сейчас не будет понятно. Но все же для информации надо завязать себе узелок на память. Теперь посмотрим на этот внутренний класс:

CW006

Собственно здесь даже о классах пока ни чего можно и не знать, достаточно просто хоть немного понимать английский язык. И так кэшируемыми значениями для значений в классе Integer являются значения от -128 до 127 по умолчанию. Но этот диапазон можно изменить при помощи опций инициализации JVM или же через аргумент запуска в командной строке. И тогда начнут происходить чудеса ��

CW001

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

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

Вообще объекты надо сравнивать через метод .equals(), который есть у каждого класса, так как он есть у класса Object, от которого наследуются все классы в Java. Но это тема для большого и отдельного разговора о наследовании и классе Object. И пока об этом говорить еще рано. В принципе, сейчас, даже эта статья еще сложна для понимания если вы только начали изучать Java, но все же ее стоит дочитать до конца, а потом когда уже у вас будет больше знаний и опыта, вернуться к ней и прочитать еще разок �� Я привел эту статью сейчас чтобы предупредить о возможных граблях.

Но это еще не все грабли �� Вас еще ждет много неожиданных сюрпризов!

К нашему классу я дописал еще несколько строк. Теперь рассмотрим их и их вывод:

CW007

CW008

Опять же тут может быть пока не понятно, что такое valueOf() и оператор new, так как это мы еще не изучали. Вкратце скажу что valueOf() это метод класса Integer, а оператор new используется для создания новых объектов.

Пока на все внимание обращаем на вывод программы. Как видим при сравнении объектов Integer содержащих одинаковые значения, созданных с использованием оператора new, даже при значениях попадающие в диапазон кэширования, мы получаем в результате false. Это происходит потому, что это ссылки intA и intB указывают на разные объекты, а при использовании valueOf(), в данном примере, ссылки intA и intB указывают на один и тот же объект. От сюда вывод, что при использовании для сравнения оператора == для ссылочных типов, сравниваются ссылки, а не объекты . Чтобы сравнить объекты надо использовать метод equals() :

println ( «Сравнение через equals() intA и intB дает » + intA . equals ( intB )) ;

Данный код нам выведет на экран: Сравнение через equals() intA и intB дает true

Теперь немного изменим значения и посмотрим на результат:

CW009

CW010

CW011

Когда мы задали значение 500, то получили вполне ожидаемый результат при сравнении ссылок. Но все же хорошо бы посмотреть на код метода valueOf().

Как видно из кода (хотя возможно вам пока это пока и не видно) при выходе за пределы значений по умолчанию метод возвращает новый объект типа Integer. Если же значение находится в пределах кэша, то оно берется от туда. Кэш из себя представляет просто массив значений типа Integer в заданном диапазоне, по умолчанию от -128 до 127.

Из всего вышесказанного следует:

  • Кэшируемыми значениями для Integer по умолчанию являются значения от -128 до 127
  • Диапазон значений по умолчанию для Integer можно изменить при помощи опций запуска JVM
  • Кэширование работает только в случае autoboxing
  • При создании объекта через оператор new кэширования не происходит
  • При сравнении объектов при помощи оператора == можно получить по морде гарблями ошибку
  • Объекты надо сравнивать при помощи метода equals()

Кэширование так же есть и для других объектов классов оберток примитивных типов:

  • Byte, Short, Long (диапазон от -128 до 127 включительно)
  • Charaster (диапазон от 0 до 127 включительно)
  • В отличие от Integer эти значения заданы жестко и изменить их параметрами запуска нельзя

Классы Float и Double не имеют механизма кэширования поскольку не могут представлять точные значения. Ну а Boolean кэшировать вообще бессмысленно.

Ну и на последок еще один примерчик и его вывод:

CW012

CW013

Хотя, опять же, многое может быть пока не понятно, но просто намотайте на ус �� ну или если нет усов завяжите узелок на память. Суть в том, что мой статический метод iHash возвращает хэш объекта, который однозначно его идентифицирует. Из вывода программы видно, что при присвоении ссылкам intA и intB значения 5, данные ссылки указывают на один и тот же объект, в данном случае находящийся в кэше. Просьба не путать хэш и кэш. А при присвоении этим ссылкам значения 500 происходит скрытый вызов оператора new и объекты создаются каждый раз новые, что видно по их хэшу, в то время как при присвоении этим ссылкам снова значения 5 они продолжают указывать, но тот же самый объект, поскольку он всегда хранится в кэше.

На этом, пожалуй, можно закончить эту статью. Как я уже и говорил, что если вы только начали изучать Java, то возможно все написанное тут будет вам не понятно. Но продолжайте читать этот блог и вы достигните просветления в Java �� и вам эта статья станет понятной ��

Размещать эту статью в другом месте блога мне представлялось мало логичным, так как она все же относится к классам оберткам для примитивных типов, которые мы уже начали обсуждать, но для понимания этой статьи нужно знать материал из последующих моих статей. Поэтому товарищи … вперед и с песней ��

Сравнение объектов в Java

Сравнение объектов является важной функцией объектно-ориентированных языков программирования. В этом руководстве мы рассмотрим некоторые функции языка Java, которые позволяют нам сравнивать объекты. Также мы обратим внимание на подобные функции во внешних библиотеках.

Операторы == и !=

Давайте начем с операторов == и != , которые могут сказать, одинаковы ли Java-объекты или нет соответственно.

Примитивы

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

Благодаря автоматической распаковке это также работает при сравнении примитивного значения с его классом-оберткой:

Если две целочисленные переменные имеют разные значения, оператор == вернет false , а оператор != вернет true .

Объекты

Предположим, мы хотим сравнить два экземпляра классов-оберток Integer с одинаковыми значениями:

При сравнении двух объектов их значения не равняются 1. Скорее различаются их адреса памяти в стеке, поскольку оба объекта создаются с использованием оператора new . Если мы присвоим переменной а переменную b , то получим другой результат:

Теперь давайте посмотрим, что происходит, когда мы используем фабричный метод Integer#valueOf :

В этом случае они считаются одинаковыми. Это связано с тем, что метод valueOf() держит Integer в кеше, чтобы избежать создания слишком большого количества оберток с одинаковыми значениями. Поэтому метод возвращает один и тот же экземпляр Integer для обоих вызовов.

Java также делает это для переменных типа String:

Однако, если они созданы с помощью оператора new , они не будут одинаковыми.

Наконец, две нулевые ссылки считаются одинаковыми, в то время как любой не-null объект считается отличным от null:

Конечно, поведение операторов равенства может быть ограничивающим. Что если мы хотим сравнить два объекта, расположенных по разным адресам и при этом полагать, что они равны на основании их внутренних состояний? Как это сделать, мы увидим в следующих пунктах.

Метод Object#equals

Теперь давайте поговорим о более широком концепте равенства с методом equals() .

Этот метод определен в классе Object , поэтому каждый объект Java наследует его. По умолчанию его реализация сравнивает адреса памяти объектов, поэтому он работает так же, как и оператор == . Однако мы можем переопределить этот метод, чтобы определить, что для наших объектов означает равенство внутренних состояний.

Во-первых, давайте посмотрим, как он ведет себя для существующих объектов, таких как Integer:

Наш метод по-прежнему возвращает true, когда оба объекта одинаковы.

Отметим здесь, что мы можем передать объект null в качестве аргумента метода, но не в качестве объекта, для которого мы вызываем метод.

Мы также можем использовать метод equals() с любым пользовательским объектом. Допустим, у нас есть класс Person:

Мы можем переопределить метод equals() для этого класса, чтобы мы могли сравнить два объекта Persons на основе их внутренних данных:

Чтобы узнать больше по этой теме, читайте вот эту статью.

Статический метод Objects#equals

Давайте посмотрим на статический метод Objects#equals . Ранее мы упоминали, что нельзя использовать null в качестве значения первого объекта, иначе будет выброшено исключение NullPointerException .

Метод equals() вспомогательного класса Objects решает эту проблему. Он принимает два аргумента и сравнивает их, а также обрабатывает значения null.

Давайте снова сравним объекты Person :

Как мы объяснили, этот метод обрабатывает значения null. Следовательно, если оба аргумента равны null, он вернет значение true, а если только один из них равен null, он вернет значение false.

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

Затем нужно обновить метод equals() , но с обработкой null значений. Мы можем сделать это, добавив условие в метод equals() :

Однако же, если мы добавим слишком много полей с возможными null значениями в класс, он может стать запутанным. Использование метода Objects#equals в реализации equals() намного чище, что улучшает читаемость кода.

Интерфейс Comparable

Логику сравнения также можно использовать для размещения объектов в определенном порядке. Интерфейс Comparable позволяет нам определять порядок между объектами, выявляя, является ли объект больше, меньше или равным другому.

Интерфейс Comparable является дженериком и имеет только один метод, compareTo() , который принимает аргумент дженерик-типа и возвращает int. Возвращаемое значение отрицательное, если this меньше аргумента, 0, в случае если они равны, и положительное в обратном случае.

Допустим, в классе Person мы хотим сравнить объекты Person по их фамилии:

Метод compareTo() вернет отрицательный int, если оно вызвано с именем Person , имеющим большую фамилию, чем this, ноль, если ту же фамилию, и положительное значение в обратном случае.

Чтобы узнать подробнее, прочитайте статью на эту тему.

Интерфейс Comparator

Интерфейс Comparator является дженериком и содержит метод compare , который принимает два аргумента этого типа и возвращает integer. Мы ранее уже видели этот шаблон с интерфейсом Comparable.

Comparator аналогичен ему; однако он отделен от определения класс. Следовательно, мы можем определить столько Comparator , сколько захотим для одного класса, где мы можем предоставить только одну реализацию Comparable .

Представим, что у нас есть веб-страница, содержащая список людей в виде таблицы, и мы хотим предложить пользователю возможность сортировать их по именам, а не по фамилиям. Это невозможно с Comparable , если мы также хотим сохранить нашу текущую реализацию, но мы можем реализовать наши собственные Comparators .

Давайте создадим Person Comparator , который будет сравнивать их только по именам:

Теперь отсортируем список людей, используя Comparator :

В интерфейсе Comparator есть и другие методы, которые мы можем использовать в реализации compareTo() :

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

Apache Commons

Давайте взглянем на библиотеку Apache Commons. Прежде всего, импортируем зависимость Maven.

Метод ObjectUtils#notEqual

Для начала поговорим о методе ObjectUtils#notEqual . Потребуется два аргумента Object , чтобы определить, не равны ли они, в соответствии с их собственной реализацией метода equals() . Он также обрабатывает значения null.

Используем наши примеры со String повторно:

Следует отметить, что у ObjectUtils есть метод equals() , что однако является устаревшим с момента возникновения Java 7, когда появились Objects#equals .

Метод ObjectUtils#compare

Теперь давайте сравним порядок объектов с помощью метода ObjectUtils#compare . Это дженерик-метод, который принимает два аргумента Comparable дженерик типа и возвращает Integer.

Приведем пример, снова используя Strings:

По умолчанию этот метод обрабатывает значения null, считая их бОльшими. Он также предлагает перегруженную версию с аргументом boolean, которая предлагает изменить это поведение и считать их мЕньшими, принимая аргумент boolean.

Guava

Давайте взглянем на Guava. Прежде всего импортируем зависимость:

Метод Objects#equal

Как и библиотека Apache Commons, Google предоставляет нам метод определения равенства двух объектов, Objects#equal . Хотя у них разные реализации, возвращают они одинаковые результаты:

Хотя он не помечен как устаревший, в JavaDoc говорится, что этот метод следует считать устаревшим, поскольку Java 7 предоставляет метод Objects#equals .

Методы сравнения

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

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

Класс ComparisonChain

Наконец, библиотека Guava предлагает класс ComparisonChain , который позволяет нам сравнивать два объекта по цепочке сравнений. Мы можем легко сравнить два объекта Person по имени и фамилии:

Базовое сравнение достигается с помощью метода compareTo() , поэтому аргументы, передаваемые методам compare() , должны быть либо примитивными, либо Comparable.

Заключение

В этой статье мы узнали о разных способах сравнения объектов в Java. Мы изучили разницу между тождественностью, равенством и порядком. Мы также рассмотрели соответствующие функции в библиотеках Apache Commons и Guava.

Полный код из статьи можно найти на GitHub.

Приглашаем всех желающих на открытое занятие «Языки статической и динамической типизации». На этом вебинаре поговорим о стилях программирования и необходимости каждого из них. Разберём основные принципы объектно-ориентированного стиля (Инкапсуляция, Наследование, Полиморфизм). Рассмотрим возможности функционального стиля, которые предоставляет язык Java. Регистрация на вебинар.

Comparing Class objects

I have to compare a Class object against a list of pre-defined classes.

Is it safe to use == or should I use equals ?

Note: I cannot use instanceof , I don’t have an object, I just have the Class object. I (mis)use it like an enum in this situation!

Lii's user avatar

6 Answers 6

java.lang.Class does not override the equals method from java.lang.Object , which is implemented like this:

So a == b is the same as a.equals(b) (except if a is null).

I am not sure if this will work for your specific situation, but you could try Class.isAssignableFrom(Class).

For the most of the Java applications this is correct. However, comparing Java classes using the operator == is safe just if both the classes are loaded by the same classloader.

Comparing two classes by its types or class names

There is need to compare two objects based on class they implement? When to compare using getClass() and when getClass().getName() ? Is there any difference between this approaches to compare two Objects class types (names)?

J.Olufsen's user avatar

8 Answers 8

or, because each class is like a singleton — there’s only one instance of each Class per class loader, and most JVMs only have the one class loader — you can even use an identity comparison:

Is there any difference between this approaches to compare two Objects class types (names)?

Yes. Two classes may have the same name if they are loaded by different ClassLoader s.

At its simplest, a class loader creates a flat name space of class bodies that are referenced by a string name.

That means it’s possible to have two classes with the same name loaded into a VM at once, provided that they have two separate ClassLoaders

When to compare using getClass() and when getClass().getName() ?

If you want to know whether two objects are of the same type you should use the equals method to compare the two classes — the first option.

I can’t imagine why you’d want to do this, but if you want to know whether two objects with different concrete types have types with the same fully qualified name, then you could use the second. If you don’t understand «concrete types» and «fully qualified names» in the context of Java then you’re not writing type analysis code for java so you don’t want to.

I ran into a problem comparing two classes using .equals. The above provided solution is not entirely accurate. Class does not implement Comparable.

Class references are not necessarily true singletons within a JVM because you can have multiple ClassLoaders.

I was writing a Maven plugin that was digging annotations out of beans after compile. The plugin had one classloader and I had my own classloader. When comparing two classes of the same name from different loaders the comparison would fail.

The implementation of Object.equals looks like this:

So you will be comparing references.

If you are comparing classes and you know for sure there will only be one classloader involved you can safely use .equals or c1 == c2 but if you are not sure you should compare by name:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *