比较预期返回类型和返回值作为字符串参数传递时方法的返回值

时间:2014-11-15 08:50:34

标签: java

为了说明我想要做的事情,请参考我之前的问题:

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 bean(pojo),我可以覆盖equals()和hashcode()来比较它。请参阅我的博客了解详情: http://javareferencegv.blogspot.com/2014/10/overriding-equals-and-hashcode-for-pojo.html

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

4 个答案:

答案 0 :(得分:1)

如果您的班级实施Comparable,您可以添加public int compareTo(Object o)功能。

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

来调用它

但请记住,该函数返回一个int。 < 0如果yourObject较小0如果它相等且> 0如果它更大。

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

答案 1 :(得分:1)

所以我在这里看到两点。

  1. 当两个Java对象被认为是相同的? 让o1和o2成为对象。当且仅当o1.equals(o2)和o2.equals(o1)时,它们是相等的; 所以你可以使用equals方法(关于空引用)。 (Lists和BigDecimals都有覆盖等于方法)

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

答案 2 :(得分:1)

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

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

    • 它是自反的:对于任何非空引用值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引用同一对象时,此方法才返回true(x == y的值为true)。
  • 请注意,通常需要在重写此方法时覆盖hashCode方法,以便维护hashCode方法的常规协定,该方法声明相等的对象必须具有相同的哈希代码。

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

编辑:

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

答案 3 :(得分: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的问题在于:

  • 如果equalsbrand字段为空,则color会将对象视为与其他任何对象不相等。
if(this.brand!=null && this.color!=null){
    ...
}else{
    return false;
}

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

  • 如果hashCodebrand为空,则color会抛出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是无效状态,那么构造函数和setter应该抛出异常,而不是让错误涓涓细流到你的equalshashCode。)