多态(polymorphism)
1、多态的形式
Java规定父类类型的变量可以接收子类类型的对象,这一点从逻辑上也是说得通的。
父类类型 变量名 = 子类对象;
父类类型:指子类继承的父类类型,或者实现的父接口类型。
所以说继承是多态的前提
//多态的引用形式
Animal a = new Cat();//Cat对象也属于其父类Animal类型。猫也是一个动物。
2、多态的表现
多态引用形式的具体表现:编译时类型与运行时类型不一致,编译时看左边的“父类”,运行时看右边的“子类”。
也就是我们常说的:编译看左边,运行看右边。
public class Book {
private String bookName;
private String author;
private double price;
public Book() {
}
public Book(String bookName, String author, double price) {
this.bookName = bookName;
this.author = author;
this.price = price;
}
public void say(){
System.out.println("这是一本书");
}
}
public class Novel extends Book{
public Novel() {
}
public Novel(String bookName, String author, double price) {
super(bookName, author, price);
}
@Override
public void say() {
System.out.println("这是一本小说");
}
public void author(){
System.out.println("这本小说的作者是张三");
}
}
public class Test {
public static void main(String[] args) {
Book book = new Novel();
book.say();
}
}
当我们想用book调用子类的author方法时出现错误
我们可以看到我们在创建对象时用的父类的类型以及子类的对象,并且在编译后实际执行的是子类Novel中的say方法,也就是运行看右边。
但当我们想用book调用子类的author方法时这就会发生错误,因为我们在编译的时候呈现的是父类的特征,而父类没有author方法,所以这就是编译看左边。
3、多态的好处
运行时,看“子类”,如果子类重写了方法,一定是执行子类重写的方法;变量引用的子类对象不同,执行的方法就不同,实现动态绑定。代码编写更灵活、功能更强大,可维护性和扩展性更好了。
提高程序的扩展性
降低类与类之间的耦合度
前面提到了当父类类型想调用子类方法时会发生错误,但是我们有时候又需要调用子类的方法,这时,我们就可以用到类型转换。
1、为什么要类型转换呢?
因为多态,就一定会有把子类对象赋值给父类变量的时候,这个时候,在编译期间,就会出现类型转换的现象。
但是,使用父类变量接收了子类对象之后,我们就不能调用子类拥有,而父类没有的方法了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做类型转换,使得编译通过。
向上转型:当左边的变量的类型(父类) > 右边对象/变量的类型(子类),我们就称为向上转型
此时,编译时按照左边变量的类型处理,就只能调用父类中有的变量和方法,不能调用子类特有的变量和方法了
但是,运行时,仍然是对象本身的类型,所以执行的方法是子类重写的方法体。
此时,一定是安全的,而且也是自动完成的
向下转型:当左边的变量的类型(子类)<右边对象/变量的编译时类型(父类),我们就称为向下转型
此时,编译时按照左边变量的类型处理,就可以调用子类特有的变量和方法了
但是,运行时,仍然是对象本身的类型
不是所有通过编译的向下转型都是正确的,可能会发生ClassCastException,为了安全,可以通过instanceof关键字进行判断
2、如何向上转型与向下转型
向上转型:自动完成
向下转型:(子类类型)父类变量
public class Test {
public static void main(String[] args) {
Book book = new Novel();
book.say();
((Novel)book).author();
}
}
3、instanceof关键字
instanceof关键字用于判断一个对象的运行时类型
为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,只要用instanceof判断返回true的,那么强转为该类型就一定是安全的,不会报ClassCastException异常。
格式:
变量/匿名对象 instanceof 数据类型
那么,哪些instanceof判断会返回true呢?
变量/匿名对象的编译时类型 与 instanceof后面数据类型是直系亲属关系才可以比较
变量/匿名对象的运行时类型<= instanceof后面数据类型,才为true
public class Type {
public void say(Book book){
book.say();
if (book instanceof Novel){
((Novel) book).author();
}
}
}
public class Test {
public static void main(String[] args) {
Book book = new Novel();
Type type = new Type();
type.say(book);
}
}