`

为什么需要重载equals?

阅读更多
关于实体类中需要重载equals的好处
Java中的基类Object已经有了equals方法,原型是

public boolean equals(Object obj){
return (this==obj);
}

    很明显,比较的标准是对象指针是否相同,也就是说,两个实体类的内部值相同,但内存位置不相同的两个对象按照Object的默认方法是不可能比较相同的,也就是说equals调用将返回false.下面我结合一下遇到的一个问题而产生的想法和解决方案.

   该系统是典型的三层结构.Webstart技术做前台-Weblogic应用服务器-Oracle数据库.我们编写的程序很明显的分为两个部分:界面-业务逻辑.关键点是,两者之间交换数据的的类:RequestEvent和ResponseEvent.Event中封装了我们需要传输的数据,我们以用例为单位,每个用例所对应的数据都可以封装成VO,我们称为值对象(value,object).


VO是我们定义的简单的数据封装,有属性,以及对属性的getter和setter方法.实现VO的结构:
public interface IValueObject extends Serializable{
}
public abstract class BaseValueObject Implements IValueObject{

}
    例如我要封装一个银行信息,来作为前后台的交互,信息有银行行别代码(人行,工行,招行...),银行代码,银行名称等.(我们假设就是这三个信息)

我们定义VO为
public class YhxxVO extends BaseValueObject{
private String yhhbdm;
private String yhdm;
private String yhmc;

//getter
public String getYhhbdm(){
  return yhhbdm;
}
...
//setter
public void setYhhbdm(String yhhbdm){
  this.yhhbdm = yhhbdm;
}
...
}

    好,我们通过RequestEvent封装我们查询条件,北京市内所有银行信息.传到后台,后台接受处理,查询数据库,得到一个银行信息列表,在返回的ResponseEvent中,我们取得一个银行信息的列表yhxxList,类型为ArrayList。 由于查找的数据表有其他大量字段或别的原因,导致查询出来的列表可能存在重复信息, 需要把yhxxList中的重复信息过滤掉,放到一个叫做result的列表中.
大概代码会是这样:(假设没有null空值需要处理)

ArrayList result = new ArrayList();
for(int i=0,size=yhxxList.size();i<size;i++){
boolean addOrNot = true;
YhxxVO yhxxvo = (YhxxVO)yhxxList.get(i);
for(int j=0,count=result.size();j<count;j++){
  YhxxVO resvo = (YhxxVO)result.get(j);
  if(resvo.getYhhbdm().equals(yhxxvo.getYhhbdm())
   && resvo.getYhdm().equals(yhxxvo.getYhdm())
   && resvo.getYhmc().equals(yhxxvo.getYhmc())
  ){
   addOrNot = false;
  }
}
if(addOrNot){
  result.add(yhxxvo);
}
}

     这是一个两重循环,而且两次定义YhxxVO变量,而且内重循环的if判断真够长的,所以严重影响可读性,以至于刚刚接手这段代码的人有点吃不透,比如,有人会提出来result是个空的,内层循环怎么进行等问题.

    熟悉ArrayList的程序员可能马上想到,使用ArrayList的contain方法,不就解决了这个不雅观的代码了嘛,是的,很对.
按照这种想法,我们可以有这样的代码:

ArrayList result = new ArrayList();
for(int i=0,size=yhxxList.size();i<size;i++){
YhxxVO yhxxvo = (YhxxVO)yhxxList.get(i);
if(!result.contain(yhxxvo)){
  result.add(yhxxvo);
}
}

完了,非常显然,这两段代码的可读性对比异常明显.

   可是,事情往往没有那么简单,运行完这段看起来不错的代码后,你会发现result和yhxxList完全一样,并没有把该过滤的重复信息过滤掉,为什么? 因为,contain函数在已有的对象中寻找是否有何参数相同的对象,判断过程的伪码大概是:

在所有的已有对象中循环{
  if(当前对象.equals(参数对象)){
   return true;
  }
}
return false;

注意,他调用的是equals,而我们的VO并没有重载equals,所以继承了Object的处理方法:比较指针!
因此,我们所有的VO都不可能相同.处理方法也变得明显-重载equals.

     但是,是不是对我们所有的VO都编写一个equals呢,显然这不是聪明的方法,所谓运筹帷幄,就是修改把equals方法重载到BaseValueObject中去,他所有的子类都可以继承他的equals方法,而不是Object的原始方法.

    困难在于,我们VO的判断值相等,却并不知道将来的子类内部都有些什么值.JAVA很精彩的一项功能现身了:反射机制.
下面是我们处理后的BaseValueObject

public abstract class BaseValueObject Implements IValueObject{
  public boolean equals(Object obj){
    if(obj==null){   //参数为空
      return false;
    }

    if(this==obj){  //和自己比较
      return true;
    }
    if(!obj.getClass().equals(getClass())){   //传入的不是同一个类型
      return false;
    }

    Method[] methods=this.getClass().getMethods();    //取得所有公共方法
    Field[] fields=this.getClass().getFields();                 //取得所有公共属性
    boolean flag=true;                                                     //标志对象是否相等
    try{
      for(int i=0;flag&&i<methods.length;i++){
        String methodName=methods[i].getName();
        if(methodName.startsWith("get")){                    //只处理get方法
          if(methodName.equals("getClass")||
             methods[i].getParameterTypes().length>0){
            continue;
          }
          String tmp=methodName.trim().substring(3);
          flag=(methods[i].invoke(this,null)).equals(methods[i].invoke(obj,null));         //对比取得两个对象的属性值是否相等
        }
      }

      for(int i=0;flag&&i<fields.length;i++){                         
        flag = fields[i].get(this).equals(fields[i].get(obj));                //对比对应的公共属性值是否相等
      }
    } catch(Exception ex){
    }
    return flag;
  }
}


我们的需求达到了,简洁的代码可以很好的工作,因为contain已经可以准确的识别出相同的银行信息了.

    为什么我们需要比较公共属性和公共方法,根据约定,如果两个值对象向外展示的属性值相同,我们就认为他们相等
当然,这种应用也只限于这种约定,也就是说我们通过我们外界可以观察到的属性来判断两者是否相等.如果你有不被人知道的私有域,且这种私有域正好成为你识别对象的标志,那这种反射机制是没有帮助的,不过仔细细想想,这样的应用恐怕极少.

好了,如果你有同样的应用和需求,希望我的这些文字能对你有所帮助!
当然任何应用和问题都有特定的领域,有特定的分析,但方法的本身还是很有裨益的.

分享到:
评论

相关推荐

    重载equals方法示例

    重载equals方法示例重载equals方法示例重载equals方法示例重载equals方法示例重载equals方法示例

    重写equals方法

    什么时候需要重写 equals 方法? ------------------------- 第一种情况:如果类中提供的 equals 方法无法满足需求,则需要重写 equals 方法。例如,在 String 类中,equals 方法用于比较两个字符串的内容是否相同...

    ==运算符和Equals()方法区别

    这也是为什么在上面的例子中,`a`和`b`的比较结果都是`true`的原因。 需要注意的是,`Equals()`方法可以被重载,以便比较自定义对象的内容是否相等。例如,在上面的例子中,我们可以重载`Person`对象的`Equals()`...

    java代码-使用java解决实现Student类的equals重载函数的源代码

    java代码-使用java解决实现Student类的equals重载函数的源代码 ——学习参考资料:仅用于个人学习使用!

    Java语言中的 覆盖重载和多态

    Java语言中的覆盖(Override)和重载(Overload)以及多态(Polymorphism)是面向对象编程...综上所述,Java中的覆盖和重载以及多态机制使得代码更加灵活,可读性和可维护性得到提升,同时也为面向对象设计提供了强大的工具。

    java 多态重载 PPT

    `equals()`方法默认比较对象的引用,但通常我们需要它比较对象的内容,因此通常需要重写。 4. `equals()`和`==`的区别 `equals()`方法在未重写时,它与`==`运算符的行为相同,比较的是对象的引用。但为了比较对象的...

    Java编程中避免equals方法的隐藏陷阱介绍

    Java编程中避免equals方法的隐藏陷阱介绍 Java编程中避免equals方法的隐藏陷阱介绍是一篇重要的技术文章,主要介绍了Java编程中避免equals...避免equals方法的隐藏陷阱需要我们小心地重载equals方法,避免常见的陷阱。

    C#中的 == 和equals()区别浅析

    若要使`Equals()`返回`true`,我们需要显式转换`age`为`short`类型,如下所示: ```csharp Console.WriteLine(newAge.Equals((short)age)); // 输出 true ``` 这里,我们通过类型转换 `(short)age` 将`age`转换为`...

    Java基础总结

    为什么要重写equals方法?为什么要重写hashCode方法?3. String s1 = new String(“abc”); String s2 = “abc”; s1 == s2? 语句1在内存中创建了几个对象?4. String为什么是不可变的?jdk源码中的String如何定义的...

    C#源代码-等号和Equals的区别.zip

    自定义类型时,通常需要重写`Equals`以实现自定义的比较逻辑。例如,如果你有一个表示矩形的类,你可能希望两个矩形被视为相等,如果它们的宽和高都相等,即使它们是不同的对象实例。 3. **`object.Equals(object)...

    面试题全集(周瑜).pdf

    * 重载和重写的区别 二、Java集合框架 * List和Set的区别 * ArrayList和LinkedList的区别 * ConcurrentHashMap的扩容机制 * HashMap的Put方法深拷贝和浅拷贝 * CopyOnWriteArrayList的底层原理 三、Java多线程...

    C#中Equals方法的常见误解

    对于引用类型,比较引用等同性是快速的,但当需要比较复杂对象时,特别是当涉及到大量字段或嵌套对象时,`Equals`的默认实现可能需要进行深度比较,这可能导致性能下降。因此,为了优化性能,开发者可能需要重写`...

    C#程序设计-3期(KC008) KC008110100003-错误的认为==和Equals作用相同.docx

    然而,如果值类型中包含引用类型字段,那么需要注意相等性检查可能需要自定义`Equals`方法来确保正确性。 总之,理解`==`运算符和`Equals`方法的差异是编写健壮的C#代码的关键。在编写代码时,根据具体情况选择合适...

    2022 最全 Java 面试笔试题汇总

    * Hashcode 和 equals 方法的联系是什么?什么是重写和重载? * Java 中 final 关键字的作用是什么?java 中的反射机制是什么? * Java 如何高效进行数组拷贝?成员变量和方法的区别是什么? Java Web * Cookie 和...

    如何在Java中避免equals方法的隐藏陷阱(上)

     本文描述重载equals方法的技术,这种技术即使是具现类的子类增加了字段也能保证equal语义的正确性。  在《Effective Java》的第8项中,Josh Bloch描述了当继承类作为面向对象语言中的等价关系的基础问题,要...

    java面试题合集1,检测知识点

    * 为什么需要线程安全? * Java中的线程池是如何工作的? * CountDownLatch和Semaphore的区别和底层原理 * ReentrantLock中的公平锁和非公平锁的底层实现 * 什么是STW?(Stop-The-World) 四、Java虚拟机 * JVM中...

    74个java面试题,只有题目没有答案

    * 为什么需要使用 Spring?Spring 提供了许多便捷的特性和工具,用于简化开发过程。 35. Spring Boot * 什么是 Spring Boot?Spring Boot 是一个基于 Spring 的子框架,用于快速开发微服务应用程序。 * 如何创建 ...

    C#中的Equals、RefrenceEquals和==的区别与联系

    如果需要比较两个struct的实例是否相等,则必须实现一个静态的Equals方法,并且可能需要重载==运算符以在代码中使用。 最后,我们通过给出的测试代码可以进一步了解这些方法和运算符在实际代码中的应用。通过不同的...

    java资料面试题

    #### 2.21 == 和 equals() 都可用于比较两个操作数是否相等,它们有什么区别吗? - `==`:比较原始类型的值是否相等,或引用类型的引用是否指向同一对象。 - `equals()`:用于比较对象的内容是否相等,通常需要在类...

    大整数加法减法 完整算法 写一个表示大整数(>2256)的对象( C++或Java),能实现 +,-,*,/(运算符重载)(

    总的来说,设计一个大整数类并实现加减运算符重载,不仅需要理解基本的算术运算,还需要掌握字符串处理、进位和借位的概念,以及在Java中如何通过类和方法来模拟运算符的行为。对于乘法和除法,可以进一步研究和实现...

Global site tag (gtag.js) - Google Analytics