【问题标题】:Compare the return value of a method when expected return type and return value are passed as string parameters当期望返回类型和返回值作为字符串参数传递时,比较方法的返回值
【发布时间】:2014-11-15 08:50:34
【问题描述】:

要提供一些关于我正在尝试做的事情的背景,请参考我之前的问题:

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;
    }

}

现在这适用于当前代码 - 返回类型是 String,我可以使用 equals 进行比较。 但是,如果该方法返回 BigDecimal 或 List 怎么办?有没有一种通用的方法可以比较多种对象类型?

我假设对于用户定义的 java beans (pojo),我可以覆盖 equals() 和 hashcode() 来比较它。详情参考我的博客: http://javareferencegv.blogspot.com/2014/10/overriding-equals-and-hashcode-for-pojo.html

任何进一步的建议表示赞赏。

【问题讨论】:

  • 是的,您可以使用equals。我想我真的不明白问题是什么。尽管由于空实例字段处理不当,您在 CarBean 中对 equalshashCode 的实现可以说是不正确的。
  • @Radiodef 看到返回类型可以是任何东西。它可能不会一直覆盖等于。另外,您认为处理空字段有什么问题?
  • 我基本上是在尝试制作一个通用代码。它将执行一个方法并将返回值与预期值进行比较。问题是,我将为每个方法运行相同的代码,并且返回类型会有所不同。所以我正在尝试制作一个可以比较任何返回类型的代码

标签: java


【解决方案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");

因此,您可以从 XML 文件创建 BigInteger。如果要创建不定义 String 构造函数的对象,则需要在文件中存储一些其他信息,告诉程序如何将其转换为对象。也许您应该研究抽象工厂模式。另一种(更复杂的)方案是序列化一个对象,然后将字节编码为 base 64,以便您可以将其存储为文本。

在我继续之前,我想指出从文本输入运行任意 Java 命令是不安全的。如果您保留允许类的白名单,这样会更安全,这样SystemFile 之类的类就不受限制。自己尝试一下也没关系,但实际上这非常不稳定,除非您采取预防措施。

关于比较的主题,您需要了解相等是一个因类型而异的概念。一般来说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)。更复杂的例程会递归地比较字段并考虑数组。

反射可能是一种合适的方法,例如反序列化的单元测试,其中一个对象可能没有定义值相等,但我们想看看它是否以正确的状态反序列化。


您的equalshashCode 的问题在于:

  • 如果brandcolor 字段为空,则您的equals 认为一个对象不等于任何其他对象。
if(this.brand!=null && this.color!=null){
    ...
}else{
    return false;
}

这意味着CarBean(null, null) 不等于CarBean(null, null)。一般equals 不会表现出这种行为。例如,通过Double#equals 比较,即使一个 NaN 也等于另一个 NaN。

  • 如果 brandcolor 为 null,您的 hashCode 将引发 NullPointerException。

更正后的实现如下:

@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;
}

(如果 null 是一个无效状态,那么构造函数和设置器应该抛出异常,而不是让错误涓涓细流到你的 equalshashCode。)

【讨论】:

    【解决方案2】:

    如果你的类实现了Comparable,你可以添加一个public int compareTo(Object o)函数。

    然后你可以使用int result = yourObject.compareTo(anotherObject);调用它

    但请记住,该函数返回一个 int。 0 如果它更大。

    您可以在compareTo(Object o) 中使用instanceOf 将您的对象与不同的对象进行比较,但您需要指定应如何处理与特定对象的比较,Java 不会为您猜测

    【讨论】:

      【解决方案3】:

      所以我在这里看到了两点。

      1. 何时认为两个 Java 对象相等? 设 o1 和 o2 为对象。它们相等当且仅当 o1.equals(o2) 和 o2.equals(o1); 所以你可以只使用 equals 方法(关于空引用)。 (Lists 和 BigDecimals 都覆盖了 equals 方法)

      2. 如果某些 POJO 不覆盖等于? 好吧,如果两个对象属于同一个类并且从所有 getter 返回的值都相等,那么您可以认为它们是相等的。那是一条危险的道路。但如果你真的想做,java.beans 包可以帮助你。

      【讨论】:

        【解决方案4】:

        所有 java 类都是 Object 类的后代。 Object 类的 Javadoc 定义了方法 equalhashcode 的通用协定。摘录:

        • equals 方法在非空对象引用上实现等价关系

          • 它是自反的:对于任何非空引用值 x,x.equals(x) 应该返回 true。
          • 它是对称的:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应该返回 true。
          • 它是可传递的:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true 并且 y.equals(z) 返回 true,则 x.equals(z) 应该返回 true .
          • 这是一致的:对于任何非空引用值 x 和 y,x.equals(y) 的多次调用始终返回 true 或始终返回 false,前提是没有修改对象上 equals 比较中使用的信息。李>
          • 对于任何非空引用值 x,x.equals(null) 应返回 false。
        • Object 类的 equals 方法实现了对象上最有区别的可能等价关系;也就是说,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象(x == y 的值为 true)时,此方法才返回 true。
        • 请注意,当 hashCode 方法被重写时,通常需要重写该方法,以维护 hashCode 方法的一般约定,即相等的对象必须具有相等的哈希码。

        这些是一般的 Java 要求,因此您可以放心地假设您测试的所有类都尊重它们。如果 Java 容器或数组 (*) 在某种意义上实现了与其元素的相等性一致的相等性,那就太好了。

        编辑:

        (*) 对于数组相等,您必须使用 Arrays.equals() - 感谢 Nikolay Ivanov 的注意

        【讨论】:

        • 你完全正确,除了数组部分。为了正确的相等,我们必须使用 Arrays.equals()。
        • @NikolayIvanov:感谢您的关注!帖子已编辑。
        猜你喜欢
        • 2021-11-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-25
        • 1970-01-01
        相关资源
        最近更新 更多