博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(Thinking in Java)第9章 接口
阅读量:6449 次
发布时间:2019-06-23

本文共 11225 字,大约阅读时间需要 37 分钟。

Thinking in Java 好书全是干货

一、抽象类和抽象方法

抽象方法:这种方法只有声明而没有方法体,下面是抽象方法生命所采用的语法

abstract void f();

包含抽象方法的类叫做抽象类,如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的,并且编译器不会允许直接创建一个抽象类对象,正确的做法是从这个抽象类继承并为抽象方法完成定义,这样就可以创建这个类的对象。但如果导出类还有抽象方法,那这个类还应该加上abstract声明为抽象类。简单的使用如下

package tij.interfacedemo;public class Test {    public static void main(String[] args) {        new Wind().play(Note.MIDDLE_A);    }}enum Note{    MIDDLE_A,MIDDLE_B,MIDDLE_C;}abstract class Instrument{//抽象父类    private int i;//    public abstract void play(Note n);    public String what(){        return "Instrument";    }    public abstract void adjust();}class Wind extends Instrument{    public void play(Note n){        System.out.println("Wind.play() "+n);    }    public String what(){        return "wind";    }    public void adjust(){            }}

其实可以发现和普通继承没什么区别。

二、接口

接口(interface)是一个完全抽象的类,没有任何具体实现方法,允许创建者创建方法的方法名、参数列表以及返回类型,但没有任何方法体。接口体现的思想是:“实现了这个接口的类看起来都像这样”。并且接口具有继承的一系列特点,如向上转型等等。

接口可以加public关键字(如果要加也只能加public,不能加private和protected),不添加就是默认访问权限(包访问权限)。接口中的方法是自动是public abstract的。接口也可以包含域(即引用变量或基本变量),并且自动是static final的,并且也不能被private和protected修饰。如下例:

package tij.interfacedemo;public class Test {    public static void main(String[] args) {        new Wind().play(Note.MIDDLE_A);    }}enum Note{    MIDDLE_A,MIDDLE_B,MIDDLE_C;}interface Instrument{//    int i=5;//    void play(Note n);    String what();    void adjust();}class Wind implements Instrument{    public void play(Note n){        System.out.println("Wind.play() "+n);    }    public String what(){        return "wind";    }    public void adjust(){            }}

三、完全解耦

这是接口的一个很好的功能

在继承中,如果一个方法接受一个类的实例作为参数,那么你可以用这个类或子类的实例当做传入参数,如下例:

package tij.interfacedemo;import java.util.Arrays;public class Test {    static void process(Processor p, Object s) {        System.out.println("Using Processor:" + p.name());        System.out.println(p.process(s));    }    static String s = "Disagreement with beliefs is by definition incorrect";    public static void main(String[] args) {        process(new Upcase(), s);        process(new Downcase(), s);        process(new Splitter(), s);    }}class Processor {    public String name() {        return getClass().getSimpleName();    }    Object process(Object input) {        return input;    }}class Upcase extends Processor {    String process(Object input) {        return ((String) input).toUpperCase();    }}class Downcase extends Processor {    String process(Object input) {        return ((String) input).toLowerCase();    }}class Splitter extends Processor {    String process(Object input) {        return Arrays.toString(((String) input).split(" "));    }}

在本例中,Test.process方法可以接受一个Processor以及其子类的实例对象,然后对一个Object的对象s进行操作,根据传入的Processor不同,进行的操作也不同,这种方法体现了策略设计模式,这类方法包含要执行的固定部分(s),策略包含变化的部分(p)。但是在这个例子中要注意两个与本章不相关的事儿:1.应该想想为什么子类明明没有重写name方法,但输出却还是像重写了一样。2.子类重写了process方法,但返回值不是Object而是String,重写方法必须要是与类方法的返回类型的相同或者是其子类。

但是假如现在我们发现了一系列滤波器类,如下:

class Waveform{//代表波形    private static long counter;    private final long id=counter++;    public String toString(){        return "Waveform"+id;    }}class Filter{//滤波器    public String name(){        return getClass().getSimpleName();    }    public Waveform process(Waveform input){        return input;    }}class LowPass extends Filter{    double cutoff;//设定低通滤波器的滤波上限    public LowPass(double cutoff){        this.cutoff=cutoff;    }    public Waveform process(Waveform input){        return input;    }}class HighPass extends Filter{    double cutoff;//设置高通滤波器的滤波下限    public HighPass(double cutoff){        this.cutoff=cutoff;    }    public Waveform process(Waveform input){        return input;    }}class BandPass extends Filter{    double lowCutoff,highCutoff;//设置带通滤波器的滤波上下限    public BandPass(double lowCut,double highCut){        this.lowCutoff=lowCut;        this.highCutoff=highCut;    }    public Waveform process(Waveform input){        return input;    }}

那么如果想把各种滤波器传给Test.process方法,那这会被编译器阻止,因为process方法只接受processor类以及子类,那如果希望运用了策略设计模式的Test.process方法仍能接受滤波器,那么首先就需要把processor改变成接口:

interface Processor {    String name() ;    Object process(Object input) ;}abstract class StringProcessor implements Processor{    public String name(){        return getClass().getSimpleName();    }}class Upcase extends StringProcessor {    public String process(Object input) {        return ((String) input).toUpperCase();    }}class Downcase extends StringProcessor {    public String process(Object input) {        return ((String) input).toLowerCase();    }}class Splitter extends StringProcessor {    public String process(Object input) {        return Arrays.toString(((String) input).split(" "));    }}class Waveform{//代表波形    private static long counter;    private final long id=counter++;    public String toString(){        return "Waveform"+id;    }}

但接下来我们发现,滤波器这个类是我们找到的,我们并不能对这么个类内的代码进行修改,如何让新的Test.process还能接受滤波器呢,于是我们可以采用适配器设计模式,代码如下:

class FilterAdapter implements Processor {    Filter filter;    public FilterAdapter(Filter filter) {        this.filter = filter;    }    public String name() {        return filter.name();    }    public Waveform process(Object input) {        return filter.process((Waveform) input);    }}

这样传进去这个FilterAdapter适配器就OK了

四、Java中的多重继承

接口的另一个重要的功能就是能实现多重继承

package tij.interfacedemo;public class Test {    public static void main(String[] args) {        Hero superman=new Hero();        superman.fight();        superman.fly();        superman.swim();    }}class ActionCharacter {    public void fight() {        System.out.println("fight");    }}interface CanFight {    void fight();}interface CanSwim {    void swim();}interface CanFly {    void fly();}class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly {    public void fly() {        System.out.println("fly");    }    public void swim() {        System.out.println("swim");    }}

五、通过继承来拓展接口

通过继承可以生成一个新的接口,以此来对原来的接口进行拓展;还可以通过继承在新接口中组合多个接口(玩的真花= =)。如下:

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 destroy() {}    public void menace() {}    public void kill() {}    public void drinkBlood() {}}

接口之间可以继承,可以多继承,可以相互拓展

1.组合接口时的名字冲突

在实现多重继承时,如果不同接口有相同方法怎么办

interface I1 {    void f();}interface I2 {    int f(int i);}interface I3 {    int f();}class C {    public int f() {        return 1;    }}class C2 implements I1, I2 {    public int f(int i) {        return 1;    }    public void f() {}//重载}class C3 extends C implements I2{    public int f(int i) {//重载        return 0;    }}class C4 extends C implements I3{    //可以不必重写int f()方法,因为从C那里继承过来了,但C那里的f()必须是public的}//class C5 extends C implements I1{//    //错误//}//interface I4 extends I1,I3{//    //错误//}

因此在设计接口的时候请尽量避免这点

六、适配接口

接口的一种常用的方法就是之前提到的策略设计模式,如编写一个进行某些操作的方法,而这个方法接收一些接口,就是说如果你的对象遵循我的接口那就能用。

下面的例子中使用了scanner,这个类需要接收一个readable对象,其中arg用来存储要输入的字符串的

import java.io.IOException;import java.nio.CharBuffer;import java.util.Random;import java.util.Scanner;public class Test {    public static void main(String[] args) {        Scanner s = new Scanner(new RandomWords(10));        while (s.hasNext()) {            System.out.println(s.next());        }    }}class RandomWords implements Readable {    private static Random rand = new Random(47);    private static final char[] capitals = "ABCDEFGHIGKLMNOPQRST".toCharArray();    private static final char[] lowers = "ABCDEFGHIGKLMNOPQRST".toLowerCase()            .toCharArray();    private static final char[] vowels = "aeiou".toCharArray();    private int count;    public RandomWords(int count) {        this.count = count;    }    public int read(CharBuffer arg) throws IOException {        if (count-- == 0) {            return -1;        }        arg.append(capitals[rand.nextInt(capitals.length)]);        for (int i = 0; i < 4; i++) {            arg.append(vowels[rand.nextInt(vowels.length)]);            arg.append(lowers[rand.nextInt(lowers.length)]);        }        arg.append(" ");        return 10;// Number of characters appended    }}

接口的还有一个功能就是之前我们提到过的适配器设计模式,在这里再举另一个与scanner相关的例子。

首先假如我们现在有一个如下的类:

class RandomDoubles{    private static Random rand =new Random(47);    public double next(){        return rand.nextDouble();    }}

希望让他作为一个readable传入给scanner,来生成随机的double类型数,这需要装饰设计模式

import java.io.IOException;import java.nio.CharBuffer;import java.util.Random;import java.util.Scanner;public class Test {    public static void main(String[] args) {        Scanner s=new Scanner(new AdaptedRandomDoubles(7));        while(s.hasNext()){            System.out.println(s.next());        }    }}class AdaptedRandomDoubles extends RandomDoubles implements Readable {    private int count;    public AdaptedRandomDoubles(int count){        this.count=count;    }    public int read(CharBuffer cb) throws IOException {        if(count--==0){            return -1;        }        String result=Double.toString(this.next());        cb.append(result);        return result.length();    }}

七、接口中的域

实例变量都是static final的,好了结束

八、嵌套接口

推荐先看完内部类再来看这个

1.类中的接口

class t1 implements A.C,A.B{//访问不到A.D    public void f() {    }}class A {    interface B {        void f();    }    public class BImp implements B {        public void f() {}    }    private class BImp2 implements B {        public void f() {}    }    public interface C {        void f();    }    class CImp implements C {        public void f() {}    }    private interface D {        void f();    }    private class DImp implements D {        public void f() {}    }    public class DImp2 implements D {        public void f() {}    }}

2.接口中的接口

interface E{    interface G{        //默认为public        void f();    }}class t2 implements E.G{    public void f() {    }}

九、接口与工厂

接口时实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法设计模式

package tij.interfacedemo;public class Test {    public static void main(String[] args) {        Factories.serviceConsumer(new Implementation1Factory());        Factories.serviceConsumer(new Implementation2Factory());    }}interface Service {    void method1();    void method2();}interface ServiceFactory {    Service getService();}class Implementation1 implements Service {    Implementation1() {}    public void method1() {System.out.println("Implementation1 method1");}    public void method2() {System.out.println("Implementation1 method2");}}class Implementation1Factory implements ServiceFactory {    public Service getService() {        return new Implementation1();    }}class Implementation2 implements Service {    Implementation2() {}    public void method1() {System.out.println("Implementation2 method1");}    public void method2() {System.out.println("Implementation2 method2");}}class Implementation2Factory implements ServiceFactory {    public Service getService() {        return new Implementation1();    }}class Factories{    static void serviceConsumer(ServiceFactory fact){        Service s = fact.getService();        s.method1();        s.method2();    }}

这样设计的好处就是将方法的实现实例对象的生成分离开来,而且在使用Factories.serviceConsumer的时候不需要特定指定是哪种Service.

转载地址:http://srlwo.baihongyu.com/

你可能感兴趣的文章
通过Application存取公共数据比如登录信息等..
查看>>
intellij maven配置与使用
查看>>
SpringMVC文件下载与JSON格式
查看>>
Q:图像太大,在opencv上显示不完全
查看>>
修正锚点跳转位置 避免头部fixed固定部分遮挡
查看>>
Dubbo序列化多个CopyOnWriteArrayList对象变成同一对象的一个大坑!!
查看>>
linux下ping不通的解决方法
查看>>
利用ItextPdf、core-renderer-R8 来生成PDF
查看>>
irc操作小记
查看>>
JAVA 与 PHP 的不同和相同
查看>>
建立Ftp站点
查看>>
NavigationController的使用
查看>>
多线程编程之Windows环境下创建新线程
查看>>
ASP.Net MVC的开发模式
查看>>
groupbox 下的datagridview的列标题字体修改混乱
查看>>
HDU-3092 Least common multiple---数论+分组背包
查看>>
CentOS 7使用systemctl如何补全服务名称
查看>>
Unity3D NGUI 给button按钮添加单间事件
查看>>
C# 使用各种API
查看>>
密码的校验.大小写字母,数字,特殊字符中的至少3种
查看>>