继承:类型强转、重写equals方法及hashCode()

发布时间 2023-09-18 11:04:12作者: ali_gaduo

类型强转

有时候需要将某个类的对象引用转换成另一个类的对象引用,就要进行类型强转。进行类型强转的唯一原因是:要暂时忽略对象的实际类型之后使用对象的全部功能。
首先要明白一点,就是父类型的变量可以引用子类型的对象,而不需要强制类型转换。需要进行强制类型转换的,都是试图让一个父类型的引用转换成子类型的引用,从而能够使用子类型的所有特性。明白这一点很重要,且看下面的代码:

class A {
    public void say() {
        System.out.println("I am A");
    }
}

class B extends A{
    public void say() {
        System.out.println("I am B");
    }

    public void greet() {
        System.out.println("Hello world.");
    }
}

// 下面是测试
        B b = new B();
        b.say();
        b.greet();
        A a = new A();
        a.say();
	a.greet();  // 报错,无法通过编译
        A ab = new B();
        ab.say();
	ab.greet();  //报错,无法通过编译
        if (ab instanceof B) ((B) ab).greet();  // 没毛病

其实根本原因是java的一个特性:动态绑定,就是一个变量它到底引用了一个什么类型,是它定义时的类型还是其子类,要等到运行才知道。在编译期,一个引用能够使用哪些特性,是由定义该引用的类型所拥有的特性所决定的。比较拗口,。如上面的代码中,对象ab(准确来讲ab是一个引用)在编译期,编译期只知道它是A类型的,隐藏它只能调用A类型的方法,而无法调用其子类型Bgreet()方法。
那么有的时候我又想看看ab在运行期间到底指向什么类型,从而在写代码的时候去调用其真正指向的类型的特性,又该怎么办呢?这个时候就要用到强制类型转换了。

将一个值存入一个变量的时候,编译期会检查你是否承诺过多,如果将一个子类的引用赋值给一个超类变量,这是没问题的,但是如果将一个超类的引用赋值给一个子类变量时,就承诺过多了。必须进行强制类型转换才能通过运行时的检查。
如果在运行的时候,试图在继承链上进行向下的强制类型转换,会发生什么呢?比如下面这样:

A a = new A();  // case 1 
B b = (B) a; //报错,ClassCastException

A ab = new B();  // case 2
B bb = (B) ab;  // OK! No problem!

重写equals方法

Object类里面定义了一个equals()方法,它比较的是两个对象的地址是否相等。如果一个类没有重写equals()方法,那么就自动调用Object类里的这个方法。但是有时候我们要比较两个类的状态,从而在逻辑上决定这两个类是否相等,就需要重写该方法。假如现在向B类里面添加两个属性:

class B {
	int id;
	String name;
}

如果两个对象的idname都相等,我就认为这两个对象相等。
那么实现的逻辑就应该如下:

@Override
public boolean equals(Object o) {
	if (o == null) return false;  // 这个容易理解
	if (o == this) return true;  // 对象地址相等,那肯定相等
	if (o instanceof B) {  // 为什么要先判断一下,上文有讲
		B other = (B) o;
		return this.id == other.id && this.name.equals(other.name);
	}
	return false;  // 如果B实际指向的对象的类型和本类型不一致,那在这里就不应该判断为相等的对象
}

重写hashCode()方法

首先要明白该方法存在的意义是什么。在Object类里面有相关的描述,该方法主要是为哈希表服务的。哈希表在判断两个对象是否相等的时候,不仅仅调用其equals()方法,而是首先查看这两个对象的hashcode是否相等,相关内容可以查看源码,比如HashMapcontains()
如果两个对象通过其equals方法判定为相等,那么这两个对象的hashcode也应该要相等
所以,如果重写了equals()方法,如果涉及到哈希表的使用,那么请一定要重写hashCode()方法,并且在生产哈希码的时候要用上equals()方法中涉及到的所有特性。比如上面的例子中,用到了idname两个特性,那么重写hashCode()方法时,就应该用上这两个特性。如果说重写的equals()方法里只用了id这一个属性,那么重写hashCode()的时候就可以只用id属性而没必要使用name属性了。