2014-11-15 3 views
-1

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

Run a java method by passing class name and method name as parameter

Поэтому в основном я пытаюсь вызвать метод и проверить его возвращаемое значение. Я прочитаю следующие параметры из xml или базы данных: класс метода, имя, параметры и возвращаемое значение. Затем я буду выполнять метод и сравнить результат.

код выглядит так, как сейчас:

public static void main(String[] args) throws IOException { 
     runTheMethod("CarBean","getColor","java.lang.String","Red"); 
    } 

    public static void runTheMethod(String className, String methodName, String expectedReturnType, Object expectedReturnValue){ 
     try { 
      Object classObj = Class.forName(className).newInstance(); 
      Method method = classObj.getClass().getMethod(methodName); 
      Object returnVal = method.invoke(classObj); 
      if(expectedReturnValue.getClass().getName().equals(expectedReturnType)){ 
// This is the problem portion 
       System.out.println("Test passed : " + expectedReturnValue.equals(returnVal)); 
      }else{ 
       System.out.println("Expected return object type does not match actual return object type"); 
      } 
     } catch (InstantiationException | IllegalAccessException 
       | ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 

Carbean это определяется пользователем POJO:

public class CarBean { 

    private String brand; 
    private String color = "Red"; 

    public CarBean(){ 
    } 

    public CarBean (String brand, String color){ 
     this.brand= brand; 
     this.color= color; 
    } 

    /** 
    * @return the brand 
    */ 
    public String getBrand() { 
     return brand; 
    } 

    /** 
    * @param the brand to set 
    */ 
    public void setBrand(String brand) { 
     this.brand= brand; 
    } 

    /** 
    * @return the color 
    */ 
    public String getColor() { 
     return color; 
    } 

    /** 
    * @param the color to set 
    */ 
    public void setColor(String color) { 
     this.color= color; 
    } 

    @Override 
    public boolean equals(Object o){ 
     if(o == null)    
      return false; 
     if(!(o instanceof CarBean)) 
      return false; 

     CarBean other = (CarBean) o; 
     if(this.brand!=null && this.color!=null){ 
      if(this.brand.equals(other.brand) && this.color.equals(other.color)) 
       return true; 
      else 
       return false; 
     }else{ 
       return false; 
     } 
    } 

    @Override 
    public int hashCode() { 
     int hash = 17; 
     hash = 31 * hash + this.brand.hashCode(); 
     hash = 31 * hash + this.color.hashCode(); 
     return hash; 
    } 

} 

Теперь это работает отлично для текущего кода - тип возвращаемого значения является строка, и я могу сравните с помощью equals. Но что, если метод возвращает BigDecimal или List? Существует ли общий способ сравнения нескольких типов объектов?

Я предполагаю, что для пользовательских java beans (pojo) я могу переопределить equals() и hashcode(), чтобы сравнить его. Ссылаясь на мой блог для деталей: http://javareferencegv.blogspot.com/2014/10/overriding-equals-and-hashcode-for-pojo.html

Любое дальнейшее предложение приветствуется.

+0

Да, вы можете использовать 'equals'. Наверное, я не совсем понимаю, в чем проблема. Хотя ваша реализация 'equals' и' hashCode' в 'CarBean', возможно, неверна из-за неправильного ввода полей нулевого экземпляра. – Radiodef

+0

@Radiodef вид возврата может быть любым. Он не может все время переопределять равные. Кроме того, что вы видите с обработкой нулевых полей? –

+0

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

ответ

1

Что касается создания объектов из строковых значений в файле XML. Вы в основном уже знаете, как это сделать. Вам просто нужно поместить имя класса для возвращаемого типа в файл XML. Затем вы можете создать ожидаемое возвращаемое значение. Например:

Object newInstance(String cls, String arg) throws LotsOfExceptions { 
    return Class.forName(cls).getConstructor(String.class).newInstance(arg); 
} 

Object obj = newInstance("java.math.BigInteger", "1000"); 

Таким образом, вы можете создать BigInteger из файла XML. Если вы хотите создавать объекты, которые не определяют конструкторы String, вам необходимо сохранить другую информацию в файле, которая сообщает вашей программе, как превратить ее в объект. Возможно, вам стоит взглянуть на абстрактные шаблоны фабрик. Другой (более сложной) схемой было бы сериализовать объект, а затем закодировать байты как базу 64, чтобы вы могли сохранить его как текст.

И прежде чем идти дальше, хочу отметить, что выполнение произвольных команд Java из текстового ввода является небезопасным. Было бы безопаснее, если бы вы сохраняли белый список разрешенных классов, чтобы классы, такие как System и File, были лишены ограничений. Это нормально, просто экспериментировать с этим самостоятельно, но на практике это очень нестабильно, если вы не принимаете меры предосторожности.

Что касается сравнений, вам необходимо понять, что равенство - это понятие, которое сильно отличается от типа к типу. В общем случае equals - это правильный способ сравнения, поскольку это равенство, которое класс определил для себя.Если класс не переопределяет equals, то он не имеет понятия равенства ценности.

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

С помощью отражения имеет проблемы, хотя:

  • Это на самом деле довольно сложно писать самостоятельно.
  • Объекты, которые определяют значение равенства через equals, могут иметь временное внутреннее состояние, которое намеренно не сравнивается. Поэтому, если мы сравним с отражением, мы пренебрегаем равенством значений, которое может быть определено классом. (И вы можете получить неожиданные результаты. Например, some versions of String calculate hashCode lazily.)

Вы можете увидеть this Q&A несколько библиотек, которые делают сравнение отражения уже.

Вот простой пример такого рода вещи, которые можно сделать:

boolean publicallyEqual(Object a, Object b) throws IllegalAccessException { 
    if(a.getClass() != b.getClass()) 
     return false; 

    for(Field f : a.getClass().getFields()) { 
     Object af = f.get(a); 
     Object bf = f.get(b); 
     if(af == null ? bf != null : !af.equals(bf)) 
      return false; 
    } 

    return true; 
} 

Этот пример сравнивает открытые поля с помощью equals, поэтому она работает для очень простых объектов (как java.awt.Point). Более сложные процедуры будут сравнивать рекурсивные поля и учитывать массивы.

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


Проблема с equals и hashCode является то, что:

  • Ваш equals считает объект не равный любому другому объекту, если их brand или color поле пустым.
if(this.brand!=null && this.color!=null){ 
    ... 
}else{ 
    return false; 
} 

Это означает, что CarBean(null, null) не равна CarBean(null, null). Обычно equals не проявляет такого поведения. Например, даже NaN равно другому NaN на сравнение Double#equals.

  • Ваш hashCode бросает NullPointerException если brand или color равна нулю.

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

@Override 
public boolean equals(Object o){ 
    if(!(o instanceof CarBean)) // also this already evaluates 
     return false;   // to false if o is null 

    CarBean other = (CarBean) o; 
    if(brand == null ? other.brand != null : !brand.equals(other.brand)) 
     return false; 
    if(color == null ? other.color != null : !color.equals(other.color)) 
     return false; 

    return true; 
} 

@Override 
public int hashCode() { 
    int hash = 17; 
    hash = 31 * hash + (brand == null ? 0 : brand.hashCode()); 
    hash = 31 * hash + (color == null ? 0 : color.hashCode()); 
    return hash; 
} 

(А если нуль является недопустимым состояние, то конструкторы и наладчики должны бросать исключения вместо того, чтобы струйка ошибки в ваших equals и hashCode.)

1

Если ваш класс реализует Comparable, вы можете добавить функцию public int compareTo(Object o).

Тогда вы можете вызвать его с помощью int result = yourObject.compareTo(anotherObject);

Но имейте в виду, что функция возвращать Int. < 0 если ваш объект меньше 0, если он равен и> 0, если он больше.

Вы можете сравнить свой объект различных объектов с помощью instanceOf внутри compareTo(Object o) но вы должны указать, как сравнение следует обращаться с конкретным объектом, Java не догадается для вас

1

Так я вижу два очка здесь.

  1. Когда два объекта Java считаются равными? Пусть o1 и o2 - объекты. Они равны тогда и только тогда, когда o1.equals (o2) и o2.equals (o1); Итак, вы можете просто использовать метод equals (относительно нулевых ссылок). (Списки и BigDecimals оба имеют метод overriden equals)

  2. Что если какой-либо POJO не переопределяет равные? Ну, вы можете подумать, что два объекта равны, если они принадлежат к одному классу, а значения, возвращаемые из всех геттеров, равны. Это опасная дорога. Но если вы действительно этого хотите, то пакет java.beans может вам помочь.

1

Все классы java являются потомками класса Object. Класс Javadoc класса Object определяет общий контракт для методов equal и hashcode.Экстракты:

  • равенства метод реализует отношение эквивалентности на непустых ссылок на объекты

    • Это рефлексивный: для любого ненулевого опорного значения х, x.equals (х) должна возвращать верно ,
    • Это симметрично: для любых ненулевых опорных значений x и y x.equals (y) должно возвращать true тогда и только тогда, когда y.equals (x) возвращает true.
    • Это транзитивно: для любых ненулевых опорных значений x, y и z, если x.equals (y) возвращает true, а y.equals (z) возвращает true, тогда x.equals (z) должно возвращать true ,
    • Согласовано: для любых непустых опорных значений x и y несколько вызовов x.equals (y) последовательно возвращают true или последовательно возвращают false, если информация, используемая при равных сравнениях с объектами, не изменяется.
    • Для любого ненулевого опорного значения х, x.equals (NULL) должен возвращать ложь.
  • Метод equals для класса Object реализует наиболее дискриминационное возможное отношение эквивалентности объектов; то есть для любых непустых опорных значений x и y этот метод возвращает true тогда и только тогда, когда x и y относятся к одному и тому же объекту (x == y имеет значение true).
  • Обратите внимание, что, как правило, необходимо переопределить метод hashCode всякий раз, когда этот метод переопределяется, чтобы поддерживать общий контракт для метода hashCode, который утверждает, что равные объекты должны иметь одинаковые хэш-коды.

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

EDIT:

(*) Для массивов равенства, вы должны будете использовать Arrays.equals() - спасибо Николаю Иванову за отметив, что

+0

Вы полностью правы, за исключением части массивов. Для правильного равенства мы должны использовать Arrays.equals(). –

+0

@NikolayIvanov: Спасибо, что заметили! Сообщение отредактировано. –

Смежные вопросы