Выяснение того, что заставило equals() вернуть false

StackOverflow https://stackoverflow.com/questions/636360

  •  10-07-2019
  •  | 
  •  

Вопрос

Как я могу узнать, что заставило equals() вернуть false?

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

Я думал об использовании объектного графика, выводе его в xml и сравнении двух объектов.Однако XMLEncoder требует конструкторов по умолчанию, jibx требует предварительной компиляции, x-stream и simple api в моем проекте не используются.Я не возражаю скопировать отдельный класс или даже пакет в мою тестовую область и использовать его там, но импортировать целую jar для этого просто не получится.

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

Есть какие-нибудь идеи, как это сделать?

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

Это было полезно?

Решение

Предположительно, это не полное сравнение графа ... если только ваши равенства не включают каждое свойство в каждом классе ... (вы можете попробовать ==:))

Попробуйте совпадения подколенного сухожилия - вы можете составить каждое сопоставление в " все из " согласовани:

Matcher<MyClass> matcher = CoreMatchers.allOf(
  HasPropertyWithValue.hasProperty("myField1", getMyField1()),
  HasPropertyWithValue.hasProperty("myField2", getMyField2()));
if (!matcher.matches(obj)){
  System.out.println(matcher.describeFailure(obj));
  return false;
}
return true;

В нем будут что-то вроде: 'ожидается, что myField1 будет иметь значение " значение " но было "другое значение" '

Конечно, вы можете встроить статические фабрики. Это немного тяжелее, чем использование apache-commons EqualsBuilder , но он дает точное описание того, что не удалось.

Вы можете создать свой собственный специализированный сопоставитель для быстрого создания этих выражений. Было бы целесообразно скопировать apache-commons EqualsBuilder здесь.

Кстати, базовая банка hamcrest составляет 32 КБ (включая исходный код!), что дает вам возможность просмотреть код и сказать вашим боссам: "Я буду придерживаться этого как своего собственного кода". (я полагаю, это ваша проблема с импортом).

Другие советы

Вы можете использовать аспекты для исправления " равного " на классах в вашем графе объектов и заставьте их регистрировать состояние объекта в файле, когда они возвращают false. Для регистрации состояния объекта вы можете использовать что-то вроде beanutils, чтобы проверить объект и выгрузить его. Это одно решение на основе jar, которое можно легко использовать в вашем рабочем пространстве

Если иерархия объектов, хранящихся в вашем дереве, достаточно проста, вы можете поместить условные контрольные точки в свои классы " равные " реализация, которая срабатывает только тогда, когда "равно" возвращает false, что ограничило бы количество шагов, в которые вы должны войти ... вы можете использовать это везде, где у вас есть доступ к отладчику. Затмение справляется с этим просто отлично.

Звучит так, будто вы хотите java-diff или что-то вроде он.

Хорошо, это совершенно странный взгляд на это, но как насчет введения нового статического метода:

public static boolean breakableEquals(Object o1, Object o2)
{
    if (o1 == o2)
    {
        return true;
    }
    if (o1 == null || o2 == null)
    {
        return false;
    }
    // Don't condense this code!
    if (o1.equals(o2))
    {
        return true;
    }
    else
    {
        return false;
    }
}

Я знаю, последний бит выглядит безумным ... но разница в том, что вы можете поставить точку останова на "return false". Если вы затем используете breakableEquals во всех ваших сравнениях глубокого равенства, вы можете прерваться, как только нажмете первую кнопку return false ".

Это не очень помогает, если вы сравниваете множество примитивных значений, по общему признанию ... но это может быть полезно. Я не могу сказать, что когда-либо использовал это, но не понимаю, почему это не сработает. Конечно, это будет немного менее эффективно - поэтому, если вы имеете дело с высокопроизводительным кодом, вы можете захотеть изменить его позже.

Другой вариант - использовать что-то вроде:

boolean result = // comparison;
return result;

Предполагая, что ваша IDE поддерживает их, вы можете затем поставить условную точку останова в операторе возврата и установить для условия " ! result ".

Еще один вариант:

public static boolean noOp(boolean result)
{
    return result;
}

Затем вы можете использовать это в сравнениях:

return Helpers.noOp(x.id == y.id) &&
       Helpers.noOp(x.age == y.age);

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

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

  

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

Хм ... что? Добавление jar в ваш путь к классам, во всяком случае, проще и менее мешает проекту, чем копирование классов или целых пакетов в виде исходного кода.

Что касается вашей конкретной проблемы, у вас есть много разных классов, которые используют много разных свойств для определения равенства, или у вас есть просто глубоко вложенный граф объектов, по существу, одинаковых классов? В последнем случае было бы очень просто просто структурировать методы equals (), чтобы можно было ставить контрольные точки на " return false " заявления. В первом случае, это может быть слишком много работы, я полагаю. Но тогда сравнение на основе XML также может не сработать, поскольку оно покажет различия между семантически равными объектами (например, множествами и картами).

Учитывая, что ваш проект не может добавить jar-файл, кажется, что это не просто ответ SO, чтобы дать целую реализацию решения, которое другие проекты требуют значительного количества кода для выполнения (и приятно включить в Jar для вас) .

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

  

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

Одним из способов решения этой проблемы является включение такой библиотеки, как Spring (которая загружает множество других jar-файлов). Я видел проект Spring, в котором фактически не использовался ни один jar-файл Spring, просто чтобы они могли использовать любой jar-файл, связанный с ним.

commons-jxpath может быть полезен для быстрого изучения дерева объектов. Я не до конца понимаю проблему включения jar-файлов, но вы можете просто использовать его в своем собственном проекте в любой IDE, в которой вы, вероятно, будете использовать выражения во время отладки.

Может быть, это Статья информация о методе отслеживания поможет вам.

Как это работает

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

Пример вывода, который они показывают, выглядит многообещающим для вашей проблемы, поскольку он показывает возвращаемое значение некоторых методов (например, isApplet):

isApplet=ложь

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

% java -jar /home/mike/java/trace.jar -путь к классу "/home/mike/jdk1.3/demo/jfc/SwingSet2/SwingSet2.jar" -исключить CodeViewer SwingSet2
|SwingSet2.()
|SwingSet2.
|SwingSet2.main([Ljava.lang.Строка;@1dd95c)
||isApplet(SwingSet2@3d12a6)
||isApplet=ложь
||SwingSet2.Создать фрейм(apple.awt.CGraphicsConfig@93537d)
||SwingSet2.createFrame=javax.swing.JFrame@cb01e3
||Создание экрана (SwingSet2@3d12a6)
|||Создать значок (SwingSet2@3d12a6, "Splash.jpg", "Splash.accessible_description")
||/createImageIcon=javax.swing.ImageIcon@393e97
|||isApplet(SwingSet2@3d12a6)
|||isApplet=ложь
|||Получить фрейм (SwingSet2@3d12a6)
|||getFrame=javax.swing.JFrame@cb01e3
|||Получить фрейм (SwingSet2@3d12a6)
|||getFrame=javax.swing.JFrame@cb01e3
||Создать экранный экран
.выполнить (SwingSet2 $ 1@fba2af)
..Показывает экран экрана (SwingSet2@3d12a6)
...isApplet(SwingSet2@3d12a6)
...isApplet=ложь
..Показывает экран экрана
.беги
||Инициализированный DEMO(SwingSet2@3d12a6)
|||Создать меню (SwingSet2@3d12a6)
||||getString(SwingSet2@3d12a6, "Панель меню.accessible_description")
|||||getResourceBundle(SwingSet2@3d12a6)
|||||getResourceBundle=java.util.PropertyResourceBundle@6989e
||||getString="Строка демонстрационного меню Swing"

XMLEncoder кодирует только свойства bean-компонентов, тогда как equals, очевидно, может работать с не-bean-компонентами и любыми внутренними полями.

Отчасти проблема в том, что вы не знаете, на что на самом деле смотрят равные. У объекта может быть много различных полей, и все же он может утверждать, что он равен другому объекту, это может быть даже другой тип. (например, пользовательский класс URL может возвращать true для строки, равной его внешней форме).

Поэтому я не думаю, что это возможно без инструментарий байт-кода , где вы можете фактически изменить функцию classes equals (), чтобы увидеть, к каким полям она обращается. Даже тогда было бы чрезвычайно трудно «по-настоящему» узнать, почему функция вернула false. Но, надеюсь, это будет простой вопрос сравнения полей, к которым на самом деле обращаются, в методе equals ()

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top