多态:在java里,多态是同一个行为具有不同的表现形式或形态的能力,就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中的实现方法,必须在由程序运行期间才能决定;
多态存在的三个必要条件:
- 继承
- 重写
- 父类引用指向子类对象
一、java多态
如下需求:使用手机扫描二维码支付时,二维码并不知道客户是通过何种方式进行支付,只有通过二维码才能判断是走哪种支付方式执行对应流程;
java实现如下:
// 支付抽象类或者接口
public class Pay {
public String pay() {
System.out.println("do nothing!")
return "success"
}
}
// 支付宝支付
public class AliPay extends Pay {
@Override
public String pay() {
System.out.println("支付宝pay");
return "success";
}
}
// 微信支付
public class WeixinPay extends Pay {
@Override
public String pay() {
System.out.println("微信Pay");
return "success";
}
}
// 银联支付
public class YinlianPay extends Pay {
@Override
public String pay() {
System.out.println("银联支付");
return "success";
}
}
// 测试支付
public static void main(String[] args) {
// 测试支付宝支付多态应用
Pay pay = new AliPay();
pay.pay();
// 测试微信支付多态应用
pay = new WeixinPay();
pay.pay();
// 测试银联支付多态应用
pay = new YinlianPay();
pay.pay();
}
// 输出结果如下:
支付宝pay
微信Pay
银联支付
比如:
Pay pay = new AliPay();
当使用多态方法调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法;
二、java的抽象类与接口类
这样实现当然是可行的,但其实有一个小小的问题,就是Pay类当中的pay方法多余了。因为我们使用的只会是它的子类,并不会用到Pay这个父类。所以我们没必要实现父类Pay中的pay方法,做一个标记,表示有这么一个方法**,子类实现的时候需要实现它就可以了。
这就是抽象类和抽象方法的来源,我们可以把Pay做成一个抽象类,声明pay是一个抽象方法。抽象类是不能直接创建实例的,只能创建子类的实例,并且抽象方法也不用实现,只需要标记好参数和返回就行了。具体的实现都在子类当中进行。说白了抽象方法就是一个标记,告诉编译器凡是继承了这个类的子类必须要实现抽象方法,父类当中的方法不能调用。那抽象类就是含有抽象方法的类。
我们写出Pay变成抽象类之后的代码:
public abstract class Pay {
abstract public String pay();
}
很简单,因为我们只需要定义方法的参数就可以了,不需要实现方法的功能,方法的功能在子类当中实现。由于我们标记了pay这个方法是一个抽象方法,凡是继承了Pay的子类都必须要实现这个方法,否则一定会报错。
抽象类其实是一个擦边球,我们可以在抽象类中定义抽象的方法也就是只声明不实现,也可以在抽象类中实现具体的方法。在抽象类当中非抽象的方法,子类的实例是可以直接调用的,和子类调用父类的普通方法一样。但假如我们不需要父类实现方法,我们提出提取出来的父类中的所有方法都是抽象的呢?针对这一种情况,Java当中还有一个概念叫做接口,也就是interface,本质上来说interface就是抽象类,只不过是只有抽象方法的抽象类。
所以刚才的Pay通过接口实现如下:
interface Pay {
String pay();
}
把Pay变成了interface之后,子类的实现没什么太大的差别,只不过将extends关键字换成了implements。另外,子类只能继承一个抽象类,但是可以实现多个接口。早先的Java版本当中,interface只能够定义方法和常量,在Java8以后的版本当中,我们也可以在接口当中实现一些默认方法和静态方法。
接口的好处是很明显的,我们可以用接口的实例来调用所有实现了这个接口的类。也就是说接口和它的实现是一种要宽泛许多的继承关系,大大增加了灵活性。
以上虽然全是Java的内容,但是讲的其实是面向对象的内容,如果没有学过Java的小伙伴可能看起来稍稍有一点点吃力,但总体来说问题不大,没必要细扣当中的语法细节,get到核心精髓就可以了。
三、Python的抽象类和接口类
在python中定义一个接口类,我们需要abc模块(抽象类基类,Abstract Base Classes)中的两个工具abstractmethod,ABCMeta,详情如下:
工具 | 说明 |
abstractmethod | 抽象类的装饰器,接口类中的接口需要使用此装饰器 |
ABCMeta | 抽象类元类 |
from abc import ABCMeta, abstractmethod # (抽象方法)
class Payment(metaclass=ABCMeta): # metaclass 元类 metaclass = ABCMeta表示Payment类是一个规范类
def __init__(self, name, money):
self.money = money
self.name = name
@abstractmethod # @abstractmethod表示下面一行中的pay方法是一个必须在子类中实现的方法
def pay(self, *args, **kwargs):
pass
class AliPay(Payment):
def pay(self):
# 支付宝提供了一个网络上的联系渠道
print('%s通过支付宝消费了%s元' % (self.name, self.money))
class WeChatPay(Payment):
def pay(self):
# 微信提供了一个网络上的联系渠道
print('%s通过微信消费了%s元' % (self.name, self.money))
class Order(object):
@staticmethod
def account(pay_obj):
pay_obj.pay()
pay1 = WeChatPay("yuan", 100)
pay2 = AliPay("alvin", 200)
order = Order()
order.account(pay1)
order.account(pay1)