接口 (thinking in Java)

本文深入探讨了Java中抽象类和接口的使用,包括如何创建抽象类和抽象方法,以及如何利用接口建立类间的协议。文章通过实例展示了抽象类、接口、继承和多态的应用,解释了它们在软件设计中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Instrument 类可以很容易转换成abstract类。既然使某个类成为抽象类并不需要所有方法都是抽象的,所以仅需要将某些方法声明为抽象即可。
下面是修改过的“管弦乐器”的例子,其中采用了抽象类和抽象方法:


enum Node{
    MIDDLE_C ,
    C_SHARP ,
    B_FLAT ;
}
//abstract关键字允许人们在类中创建一个或多个没有任何定义的方法
abstract class Instrument{
    private int i;
    public abstract void play(Node n);
    public String what(){
        return "Instrument";
    }
    public abstract void adjust();
}
class Wind extends Instrument{
    public void play(Node n){
        System.out.println("Wind.play()" + n);
    }
    public String what(){
        return "Wind";
    }
    public void adjust(){}
}
class Percussion extends Instrument{
    public void play(Node n){
        System.out.println("Percussion.play()" + n);
    }
    public String what(){
        return "Percussion";
    }
    public void adjust(){}
}
class Stringed extends Instrument{
    public void play(Node n){
        System.out.println("Stringed.play()" + n);
    }
    public String what(){
        return "Stringed";
    }
    public void adjust(){}
}
class Brass extends Wind{
    public void play(Node n){
        System.out.println("Brass.play()" + n);
    }
    public void adjust(){
        System.out.println("Brass.adjust()");
    }
}
class Woodwind extends Wind{
    public void play(Node n){
        System.out.println("Woodwind.play()" + n);
    }
    public String what(){
        return "Woodwind";
    }
}
public class Music4 {
    //多态
    static void tune(Instrument i){
        i.play(Node.MIDDLE_C);
    }
    static void tuneAll(Instrument[] e){
        for(Instrument i : e){
            tune(i);
        }
    }
    public static void main(String[] args){
        Instrument[] orchestra = {
            new Wind(),new Percussion(),new Stringed(),new Brass(),new Woodwind()
        };
        tuneAll(orchestra);
    }
}

创建抽象类和抽象方法非常有用,因为他们可以使类的抽象性明确起来,并告诉用户和编译器打算怎么来使用他们。

接口

interface关键字使抽象的概念更向前迈进了一步。abstract关键字允许人们在类中创建一个或多个没有任何定义的方法——提供了接口部分,但是没有提供任何相应的具体实现,这些实习是由此类的继承者创建的。interface这个关键字产生一个完全抽象的类,它根本没有提供任何具体实现。它允许创建者确定方法名、参数列表和返回类型,但是没有任何方法体。接口只提供了形式,从未提供任何具体实现。

一个接口表示:“所以实现了该特定接口的类看起来都像这样”。因此,任何使用某特定接口的代码都知道可以调用该接口的哪些方法,而且仅需要知道这些。因此,接口被用来建立类与类之间的协议。

要想创建一个接口,需要用interface关键字来替代class关键字,就像类一样,可以在interface关键字前面添加public关键字。如果不添加public关键字,则它只具有包访问权限。接口也可以包含域,但这些域隐式地是static和final的。

要让一个类遵循某个特定的接口,需要使用implement关键字,它表示:“interface只是它的外貌,但是现在我要声明它是如何工作的”。除此之外,它看起来还很像继承
在这里插入图片描述
可以从Woodwind和Brass看出来,一旦实现了某个接口,其实现就变成了一个普通的类,可以按常规方式扩展它

enum Node{
    MIDDLE_C ,
    C_SHARP ,
    B_FLAT ;
}
interface Instrument{
    int value = 5; //static 和 final
    //无方法定义
    void play(Node n);//自动是public访问权限
    void adjust();
}
class Wind implements Instrument{
    public void play(Node n){
        System.out.println(this + ".play()" + n);
    }
    public String toString(){
        return "Wind";
    }
    public void adjust(){
        System.out.println(this + ".adjust()");
    }
}
class Percussion implements Instrument{
    public void play(Node n){
        System.out.println(this + ".play()" + n);
    }
    public String toString(){
        return "Percussion";
    }
    public void adjust(){
        System.out.println(this + ".adjust()");
    }
}
class Stringed implements Instrument{
    public void play(Node n){
        System.out.println(this + ".play()" + n);
    }
    public String toString(){
        return "Stringed";
    }
    public void adjust(){
        System.out.println(this + ".adjust()");
    }
}
class Brass extends Wind{
    public String toString(){
        return "Brass";
    }
}
class Woodwind extends Wind{
    public String toString(){
        return "Woodwind";
    }
}
public class Music4{
    static void tune(Instrument i){
        i.play(Node.MIDDLE_C);
    }
    static void tuneAll(Instrument[] e){
        for(Instrument i : e){
            tune(i);
        }
    }
    public static void main(String[] args){
        Instrument[] orchestra = {
                new Wind(),new Percussion(),new Stringed(),new Brass(),new Woodwind()
        };
        tuneAll(orchestra);
    }
}

what()方法已经被修改为toString()方法,因为toString()逻辑正是what()要实现的逻辑,由于toString()方法是跟类Object的一部分,因此它不需要出现在接口中。
无论是将其向上转型为称为Instrument的普通类还是抽象类,亦或是称作Instrument的接口,都不会有问题。它的行为都是相同的。

9.5通过继承来扩展接口
通过继承,可以很容易地在接口中添加新的方法声明,还可以通过继承在新接口中组合数个接口。这两种情况都可以获得新的接口,如下:

interface Monster{
    void menace();
}
interface DangerousMonster extends Monster{
    void destroy();
}
interface Lethal{
    void kill();
}
class DragonZilla implements DangerousMonster{
    public void menace(){}
    public void destroy(){}
}
interface Vampire extends DangerousMonster , Lethal{
    void drinkBlood();
}
class veryBadVampire implements Vampire{
    public void menace(){}
    public void destroy(){}
    public void kill(){}
    public void drinkBlood(){}
}
public class HorrorShow {
    static void u(Monster b){b.menace();}
    static void v(DangerousMonster d){
        d.menace();
        d.destroy();
    }
    static void w(Lethal l){
        l.kill();
    }
    public static void main(String[] args){
        DangerousMonster barney = new DragonZilla();
        u(barney);
        v(barney);
        Vampire vlad = new veryBadVampire();
        u(vlad);
        v(vlad);
        w(vlad);
    }
}

DangerousMonster是Monster的直接扩展,它产生了一个新接口。DragonZilla中实现了这个接口。
在Vampire中使用的语法仅适用于接口继承。一般情况下,只可以将extends用于单一类,但是可以用于多个基类接口。就像所看到的,只需用逗号将接口名一一分隔开。

初始化接口中的域
在接口中定义的域不能是“空final”,但是可以被非常量表达式初始化。

interface RandVal{
    Random RAND = new Random(47);
    int Rand_int = RAND.nextInt();
    long Rand_long = RAND.nextLong();
    float Rand_float = RAND.nextFloat();
    double Rand_double = RAND.nextDouble();
}

既然域是static的,它们就可以在类第一次被加载时初始化,这发生在任何域首次被访问时。

import java.util.Random;

interface RandVal{
    Random RAND = new Random(47);
    int Rand_int = RAND.nextInt();
    long Rand_long = RAND.nextLong();
    float Rand_float = RAND.nextFloat();
    double Rand_double = RAND.nextDouble();
}
public class TestRandVal {
    public static void main(String[] args){
        System.out.println(RandVal.Rand_int);
        System.out.println(RandVal.Rand_long);
        System.out.println(RandVal.Rand_float);
        System.out.println(RandVal.Rand_double);
    }
}

当然这些域不是接口的一部分,它们的值被存储在该接口的静态存储区域。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值