Java类和对象(四)之抽象编程

本文将学习抽象编程概念进而引申出抽象类和接口,然后介绍抽象类和接口的使用,接着会介绍多重继承,最后会比较抽象类和接口以及各自的使用场景。

抽象编程

抽象编程就是把一组类的共有方法提取到一个基类里面,我们通过这个基类控制这组类而不用关心这组类的实现细节,我们以上一篇中移动NPC为例:

package example;
class Spirit{
    public void move(){
        System.out.println("通用移动动画");
    }
}
//乔峰
class Qiaofeng extends Spirit{
    public void move(){
        System.out.println("乔峰移动动画");
    }
}
//段誉
class Duanyu extends Spirit{
    public void move(){
        System.out.println("段誉移动动画");
    }
}

class Controller{
    public Controller(Spirit spirit){
        s = spirit;
    }
    //控制左移
    public void leftKeyDown(){
        //
        //TODO::一些碰撞检测等代码
        //
        s.move();
    }
    private Spirit s;
}
public class TestClass {
    public static void main(String[] args){
        Controller c1 = new Controller(new Qiaofeng());
        c1.leftKeyDown();

        Controller c2 = new Controller(new Duanyu());
        c2.leftKeyDown();
    }
}

例子中Controller类使用了抽象编程,我们把NPC乔峰和段誉的共有方法move提取到基类Spirit里面, Controller类通过Spirit 对象来操作所有的NPC。抽象编程有良好的可扩展性和可复用性,比如增加一个虚竹的NPC只需创建虚竹类并继承自Spirit类然后实现move方法Controller类就可以操作虚竹了。 这里的 Spirit 类是一个普通类,它还可以是抽象类也可以是接口,抽象类和接口怎么使用呢?

抽象类

在创建类时增加关键字abstract即可创建抽象类,相比普通类抽象类具有两个特点:

  • 抽象类不能实例化对象
  • 可以添加abstract方法,这种方法不能提供方法实现,只有继承抽象类的子类能实现这些方法

以下示例说明了抽象类的特点:

//类声明前添加abstract关键字即可创建抽象类
abstract class Spirit{
    //abstract修饰的方法不能提供方法实现
    abstract public void move();
}

//不能实例化抽象类的对象,以下代码是非法的
//Spirit s = new Spirit()

接口

接口使用interface关键字声明,接口里面的方法有抽象方法和非抽象方法,非抽象方法用default关键字修饰,抽象方法没有default修饰, 抽象方法不能提供实现代码,非抽象方法可以提供实现并且可以被实现类使用。接口方法默认是public访问权限,在实现接口时需要显示的什么public。接口也可以包含数据,但是接口里面的数据默认是static final常量,接口无法放置实例数据。和抽象类一样接口不能实例化对象。

interface Spirit{
    //接口里面的数据默认是static final
    int a = 0;
    //默认是public访问权限
    void move();
    default void doSomething(){
        //......
    }
}
//不能实例化抽象类的对象,以下代码是非法的
//Spirit s = new Spirit()

可以创建一个类来实现接口,这个类可以向上转型为接口对象。

class QiaoFeng implements Spirit{
    public void move(){
        //
    }
}
Spirit s = new QiaoFeng();
s.move();

一个类可以实现多个接口,通过这个机制可以实现多重继承

interface Spirit{
    void move();
}
interface Hero{
    void dazhao();
}
class QiaoFeng implements Spirit, Hero{
    public void move(){
        System.out.println("移动");
    }
    public void dazhao() {
        System.out.println("释放降龙十八掌");
    }
}
public class TestClass {
    public static void main(String[] args){
        QiaoFeng q = new QiaoFeng();
        Spirit s = q;
        Hero h = q;
        s.move();
        h.dazhao();
    }
}

抽象类 vs 接口

抽象类和接口都是用来实现抽象编程,那么什么时候使用抽象类什么时候使用接口呢?我们先分别探讨抽象类和接口存在的意义。Java已经有类了,为什么还需要抽象类,从抽象类的特点出发,抽象类有抽象方法和非抽象方法,抽象方法由子类提供具体的实现且每个子类的实现不一样。非抽象方法一般被所有子类共用,是一个类继承结构的公共代码。我想抽象类存在的意义是:

  • 抽象方法机制强制子类必须提供实现代码否则编译器会报错,这样可以避免新的子类忘记实现抽象方。
  • 非抽象方法机制可以把类继承体系里面的公共部分代码提取出来。

相比于抽象类,接口没有实例相关的数据,所以没有办法把子类( 实现类 )的公用数据放到接口里面,从而也没法把数据相关的一些公用方法提取到接口里面。有一种办法可以把公用代码和数据提取出来,创建一个类继承结构把公用代码和数据放到根类里面,所有子类继承自根类并且实现接口。示例如下:

interface Spirit{
    void move();
}

class SpiritCommon{
    int commonData;
    void commonMethod(){
        //......
    }
}
class QiaoFeng extends SpiritCommon implements   Spirit{
    public void move(){
        commonMethod();
    }
}

子类有公用的实例数据这种应用场景不适合使用接口,它使代码变得复杂化,应该用类或者抽象类代替之。接口的优点在于多继承机制,它使接口具有很好的可扩展性或者适应性,不同继承体系里面的类可以通过适配器来适配到一个接口。示例如下:

//Controller使用接口进行抽象编程
interface Spirit{
    void move();
}
class Controller{
    public void keyDown(Spirit s){
        s.move();
    }
}
//一个类继承结构Animal和Cat
class Animal{
}
class Cat extends Animal{
}

示例中Controller类使用Spirit接口进行抽象编程,另外还有一个继承结构Animal和Cat,怎么让
Controller操作Cat类呢?答案是给Cat封装一个适配器类,示例如下:

class CatAdapter extends Cat implements Spirit{
    public void move(){
        System.out.println("cat move");
    }
}
public class TestClass {
    public static void main(String[] args){
        Controller ctrl = new Controller();
        CatAdapter ca = new CatAdapter();
        ctrl.keyDown(ca);
    }
}

可以看到经过适配以后,Controller类可以间接的操作Cat类。如果Spirit是抽象类则没有办法使Controller类操作Cat类,这就是使用接口进行抽象编程的优点:扩展性更好。

什么时候使用抽象类什么时候使用接口呢?原则是优先使用类或者抽象类,除非有足够的理由需要扩展性才考虑使用接口。接口虽然扩展性更好,但是接口无法放置共用的实例数据及相关的方法的缺点会使代码复杂度变高,而且实际开发中共用实例数据这种场景很常见,所以我们优先使用类或者抽象类,即使以后有扩展性需求我们再把代码重构成接口即可。

最后

抽象编程就是使用一个通用的类来操作一组类,抽象编程可以提高代码的复用性和扩展性。实际开发中接口容易被滥用,这是过度设计的一种体现,当有充足的理由时才应该使用接口,因为使用接口会使代码的复杂性变高。

 

 

【水煮Java】

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值