比较两个对象的属性

时间:2018-08-22 20:18:04

标签: java

我有两个对象,需要比较它们的属性。该代码当前使用很长的if / else语句序列来逐步访问属性。

要解决这个问题吗?

例如,它看起来像:

 if (car.getIsElectric() && (!parkingGarage.getIsElectric())) {
   log("Electric cars are not permitted here");
   return 1;
 }

 if (car.getIsSuv() && (!parkingGarage.getIsSuv())) {
   log("SUVs are not allowed in.");
   return 1;
 }
 ...

此问题类似于: Comparing the properties of two objects 我想看看Java是否存在等效形式。

4 个答案:

答案 0 :(得分:1)

您可以反射性地遍历对象中的每个字段。我的示例假定这两个对象具有相同的确切类型,但是我敢肯定,您可以对其进行改进以比较同一类层次结构中的两个不同对象。

public class Vehicle {

    public int width, height;

    public Vehicle(int int1, int int2) {
        this.width = int1;
        this.height = int2;
    }

    public static void main(String[] args) {
        Vehicle vehicle1 = new Vehicle(5, 10), vehicle2 = new Vehicle(5, 10), vehicle3 = new Vehicle(5, 20);

        System.out.println(equal(vehicle1, vehicle2));
        System.out.println(equal(vehicle2, vehicle3));
        System.out.println(equal(vehicle1, vehicle3));
    }

    public static boolean equal(Vehicle first, Vehicle other) {
        for (Field f : Vehicle.class.getDeclaredFields()) {
            try {
                if (!f.get(first).equals(f.get(other)))
                    return false;
            } catch (IllegalArgumentException e) {
                // Method callers can only pass in Test objects to this method, so this
                // shouldn't be thrown either. Watch out for callers passing in null to this
                // method though.
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // Fields are public and this method belongs to Test, so this shouldn't be
                // thrown.
                e.printStackTrace();
            }
        }
        return true;
    }

}

以上代码的主要部分是静态equal方法。它比较每个对象上的每个Field(在Vehicle 中声明的),如果不相等,则返回false。如果您还想比较Vehicle继承字段,则可以使用getFields()代替getDeclaredFields()

简而言之,我认为您正在寻找的代码类型是:

for (Field f : Vehicle.class.getDeclaredFields())
    try {
        if (!f.get(first).equals(f.get(other)))
            return false;
    } catch (IllegalArgumentException | IllegalAccessException e) {
        e.printStackTrace();
    }
return true;

(顺便说一句,我更喜欢“无限序列”:))

答案 1 :(得分:0)

注意: 该答案适用于原始问题,比较两个相同类型的对象,不适用于检查访问权限的更新问题规则


要比较带有大量if语句的两个对象,我们将使用一个带有3个字段的Vehicle对象作为示例:

class Vehicle {
    private final String  type;
    private final int     wheelCount;
    private final boolean electric;
}

您已经知道,您必须为每个写一个setter(或构造函数)和getter,所以您已经有“重复性工作”要做,所以请放心:

    public Vehicle(String type, int wheelCount, boolean electric) {
        this.type = type;
        this.wheelCount = wheelCount;
        this.electric = electric;
    }
    public String getType() {
        return this.type;
    }
    public int getWheelCount() {
        return this.wheelCount;
    }
    public boolean isElectric() {
        return this.electric;
    }

和标准方法:

    @Override
    public int hashCode() {
        int hash = this.type.hashCode();
        hash = hash * 31 + this.wheelCount;
        hash = hash * 3 + (this.electric ? 1 : 2);
        return hash;
    }
    @Override
    public boolean equals(Object obj) {
        if (! (obj instanceof Vehicle))
            return false;
        Vehicle that = (Vehicle) obj;
        return (this.type.equals(that.type)
             && this.wheelCount == that.wheelCount
             && this.electric == that.electric);
    }

所以现在您可以简单地做:

if (! car.equals(bus)) {
    return 1;
}

那是唯一的if语句。

答案 2 :(得分:-1)

您需要做的第一件事就是让您的类实现Comparable Interface。如果您的类创建复合对象(包含其他对象的对象),则这些对象中的每个对象也必须实现可比较的接口。您需要做的第二件事是重写Object.equals(Object)Object.hashCode()方法。您的示例正在创建一个静态equals方法,我强烈建议您不要这样做。就像我之前为实现Comparable所提到的那样,所有对象引用的类都必须覆盖equalshashCode

可比接口只有一个方法:compareTo(T t)。此方法的目的是比较两个对象,以确定两个对象是否相似(但不相同)。例如,如果两个车辆的制造商/型号相同,制造年份相同,具有相同的选件,发动机,车轮等,但具有不同的VIN编号,则可以说它们是相似的。基本上,这两辆车的外观相同,但并非完全相同。同时,equalshashCode方法的目的是建立对象实例相等性。

下面以相同的示例为例,您说您报警是因为您的车辆被盗了。警察会收集有关您汽车的所有信息,他们将调查所有与车辆描述相符的汽车(看起来像您的汽车)。但是,通过查找VIN号,他们会确定该车不是您的车。所以基本上....

yourVehicle.compareTo(suspiciousVechile); // return true
yourVehicle.equals(suspiciousVehicle); // returns false

尽管在Comparable接口Javadoc中不建议这样做,但是在某些情况下,compareTo的真实结果可能会产生与equals不同的结果。这适用于一个对象是另一个对象的副本,但它们是不同实例的情况。有时候这个事实很重要;尽管通常不是。

我建议您研究一下Java的String类,看看它们如何同时使用两种方法(compareTo和equals)。忽略equals在内部不调用compareTo的事实。

答案 3 :(得分:-1)

对于两个任意对象,您可以反射性地迭代其中一个对象中的字段,以查看两个对象均具有1:1的字段。然后,您可以比较这些字段。

有关快速解决方案,请检查以下内容。

这是一个非常大且可能令人讨厌的示例:

class Test {

    public static Field getFieldSafely(String fieldName, Object object) {
        Field[] fields = object.getClass().getFields();
        for (Field f : fields)
            if (f.getName().equals(fieldName))
                return f;
        return null;
    }

    public static boolean equal(Object first, Object other) {
        for (Field f : first.getClass().getFields()) {
            String fieldName = f.getName();
            Field otherField = getFieldSafely(fieldName, other);
            if (otherField != null)
                try {
                    if (!f.get(first).equals(otherField.get(other)))
                        return false;
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    e.printStackTrace();
                }
        }
        return true;
    }

    public static void main(String[] args) {
        Vehicle vehicle1 = new Vehicle(5, 10), vehicle2 = new Vehicle(5, 10), vehicle3 = new Vehicle(5, 20);
        Box box1 = new Box(5, 10), box2 = new Box(100, 200);

        System.out.println(equal(vehicle1, vehicle2));// True
        System.out.println(equal(vehicle2, vehicle3));// False
        System.out.println(equal(vehicle1, vehicle3));// False
        System.out.println(equal(box1, box2));// False
        System.out.println(equal(vehicle1, box1));// True
    }

}

class Box {
    public int width, height;

    public Box(int width, int height) {
        this.width = width;
        this.height = height;
    }

}

class Vehicle {

    public int width, height, weight;

    public Vehicle(int int1, int int2) {
        this.width = int1;
        this.height = int2;
    }

}

最重要的方法在Test类中。方法equal(...)接受两个对象。然后,它检查第一个对象中的每个字段是否包含在第二个对象中。对于它在第一个对象中找到的,与第二个对象中的字段同名的每个字段,将检查这些字段是否相等。如果在两个对象中找到的所有字段都相等,则该方法返回true。否则,它将返回false

这是重要的方法。您可以根据需要修改它们,以满足您的需求。

如果在给定对象中找不到具有给定名称的字段,则

getFieldSafely(...)仅返回null。否则,它将返回该字段。

public static Field getFieldSafely(String fieldName, Object object) {
    Field[] fields = object.getClass().getFields();
    for (Field f : fields)
        if (f.getName().equals(fieldName))
            return f;
    return null;
}

public static boolean equal(Object first, Object other) {
    for (Field f : first.getClass().getFields()) {
        String fieldName = f.getName();
        Field otherField = getFieldSafely(fieldName, other);
        if (otherField != null)
            try {
                if (!f.get(first).equals(otherField.get(other)))
                    return false;
            } catch (IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }
    }
    return true;
}

祝你好运。 :)