不太理解equals(Object student)

来源:2-5 使用自定义类测试我们的算法

weixin_慕莱坞8023553

2022-06-09 12:20:38

https://img.mukewang.com/climg/62a1720f09eeaa4219421434.jpg

老师您好,我还是不太理解为什么第10行要用“Object student" 而不是“Student student" 作为参数。听您讲是因为equals属于Object的method,是吗?可是为什么我们不能直接重新给Student写一个method、命名为equals呢?

写回答

1回答

liuyubobobo

2022-06-09

首先你可以尝试一下把代码中的 Object 改成 Student 试试看?你会看到在 @Override 上显示报错。


为什么?因为我们是想覆盖父类(Object 类)中的 equals 方法,如果想要覆盖一个方法,函数签名就必须完全一致。在 Object 类中,equals 方法的定义就是 boolean equals(Object),所以,我们在 Student 类中,要想覆盖这个方法,也就必须使用这个函数签名。


=========


可是为什么我们不能直接重新给Student写一个method、命名为equals呢?



这个是个好问题。


我们可以给 Student 类写一个单独的 equals,如下所示(注意要删除 @Override,才会没有编译错误)


public class Student {
    private String name;
    public Student(String name){
        this.name = name;
    }
    public boolean equals(Student student){
        return this.name.equals(student.name);
    }
}


现在你可以使用这一版的 Student,运行课程的 LinearSearch 的测试用例(代码在这里:https://git.imooc.com/class-105/Play-Algorithms-and-Data-Structures/src/master/02-Linear-Search/05-Using-Custom-Class/src/LinearSearch.java  )

你会发现,res3 的结果是错误的。res3 会得到 -1,也就是在 students 数组中,找不到叫 Bobo 的 student。


如果你在此时的 Student 的 equals 中添加断点,会发现进入不了这个 equals。

如果你使用 IntelliJ IDE 在运行 if(data[i].equals(target)) 这句话的时候强制进入 equals 函数内部,会发现进入了 Object 的 equals 内部,而不是你定义的这个 Student 的 equals 内部。


为什么,这里涉及一些比较高级的 Java 语法概念了。如果你是一个 Java 初学者,不理解没有关系。(基本上任意一本 Java 高级教材都会介绍如下的概念。)


==========


简单来说, boolean equals(Object student) 叫 Override,而 boolean equals(Student student) 叫 Overloading。Java 语言处理这两种方式是不一样的。要想让 Java 语言调用 Overloading 的方法,必须使用静态类型(即显式声明的类型)。


而在我们的 LinearSearch 中,传入的参数的类型是泛型,泛型不是静态类型,泛型的具体类型在运行时才被解析出来,所以叫做运行时类型(run-time type)。Java 无法调用非静态类型的 Overloading 的方法,所以会找到 Object 上。但 Java 可以调用非静态类型 Override 的方法,所以我们如果声明成 boolean equals(Object student),是可以进去的。


=========


顺便提一句,不仅仅是我们自己设计的 LinearSearch 会如此,Java 标准库中的所有的容器类,都包含同样的问题。如果我们想让自定义的类按照我们自己的原则做 equals 的逻辑,必须 Override Object 的 equals,也就是参数必须是 Object 的。


比如你可以写这么一个 Pojnt 类:


public class Point {
    private final int x;
    private final int y;
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public int getX() {
        return x;
    }
    public int getY() {
        return y;
    }
    
    // 注意: 这个 equals 不是 override,而是 overloading 
    public boolean equals(Point other) {
      return (this.getX() == other.getX() && this.getY() == other.getY());
    }
}


你会发现在下面的代码中,虽然 p1 和 p2 一样,但是加入 p1 的哈希表是找不到 p2 的。


Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);

HashSet<Point> hashSet = new HashSet<Point>();
hashSet.add(p1);

System.out.println(hashSet.contains(p2)); // prints false


原因同理。你再试试把 Point 类中的 equals 按照课程方法改写成 override 的形式试试看?


=========


最后,值得一提的是,对于 equals 方法(hashcode 方法等等 Object 类中的方法),在大多数情况下,我们都是希望 override 他们,而非 overload 他们的,所以即使对上面介绍的原理一知半解,把 Override 这些方法而非 overload 这些方法作为编程的原则,在 99.99% 的情况下不会出问题。(实际上我暂时基本上想不到会出问题的地方,留 0.01% 的富裕只是不把话说死而已:))


继续加油!:)

1
heixin_慕莱坞8023553
hp>明白了,非常感谢老师的解答!



h022-06-09
共1条回复

算法与数据结构

波波老师5年集大成之作,算法与数据结构系统学习,考试、面试、竞赛通用

2603 学习 · 1086 问题

查看课程