接口#

抽象类和抽象方法#

抽象方法仅有声明,没有方法体,如 abstract void f();

包含抽象方法的类必须限定为抽象类,如 abstract class ClassName {},我们创建抽象类的目的是希望通过这个通用接口操纵一系列类。

如果从一个抽象类继承,并想创建该新类的对象,那么就必须为基类中的所有抽象方法提供方法定义。 如果不这样做(可以选择不做),那么导出类便也是抽象类,且编译器将会强制我们使用 abstract 关键字来限定这个类。

接口定义#

抽象方法可以提供接口,抽象方法所属的类是 部分抽象 的类(因为抽象类中可以有某些方法的实现)。但是 interface 关键字可以产生一个 完全抽象 的类,如 interface ClassName {}

接口和类一样,也有访问权限控制,如果你想让这个接口具有全局访问性,那么用 public 声明它,否则它只有包访问权限。 在接口中声明的方法,若不加以声明,默认都是 public 访问权限。 在接口中声明的内部类,若不加以声明,默认都是 public static 的。 在接口中声明的属性,若不加以声明,默认都是 public static final 的。

接口被用来建立类与类之间的 协议。 要让某个类遵循某个特定协议(或者一组协议),implements 它即可。

使用接口的核心原因是为了能够向上转型为多个基类型(以及由此带来的灵活性)。 事实上,如果知道某个事物应该成为一个基类,那么第一选择应该是使它成为一个接口。 接口是一种重要的工具,但是它们容易被滥用,恰当的原则应该是优先选择类而不是接口。

任何抽象性都应该是应真正的需求而产生的。 当必需时,你应该重构接口而不是到处添加额外级别的间接性,而这又会带来额外的复杂性。

接口和内部类为我们提供了一种将 接口与实现分离 的更加结构化的方法。

完全解耦#

当一个系统足够大时,解耦应该是一个必然会聊到的话题,而选择不同的实现方式,解耦能力也不同。 如果使用继承,则无法完全解耦,而使用接口,则可以实现完全解耦。

由于单根继承结构的局限性,在使用继承语法的实现中,若想要利用多态的特性,我们只能调用基类中的方法。 因此,基类和导出类之间是一对多的树形关系。

而在使用接口语法的实现中,摆脱了单根继承结构的局限性,赋予了 Java “多重继承” 的能力。 一个类可能实现了多个接口,那么我们调用任何一个接口中的任何一个方法,都可以动态绑定到实现类上。 因此,接口类和实现类之间是多对多的网状关系,大大扩展了解耦和扩展能力。

语言表述总是难以给人一个很直观的印象。若我们有一个符合如下继承关系的几个类:

classDiagram Processor <|-- Upcase : extends Processor <|-- Downcase : extends Processor <|-- Splitter : extends Processor : String name() Processor : Object process() Upcase : String process() Downcase : String process() Splitter : String process() class Apply Apply : void process(Processor p, Object s)
classDiagram Filter <|-- LowPass : extends Filter <|-- HighPass : extends Filter <|-- BandPass : extends Filter : String name() Filter : Waveform process() LowPass : Waveform process() HighPass : Waveform process() BandPass : Waveform process() class Waveform Waveform : long counter Waveform : long id Waveform : String toString()

FilterProcessor 具有相同的接口元素 process(),但是因为 Filter 并非继承自 Processor,因此在调用 Apply.process() 时,并不会触发 Filter 类的 process() 方法。 这主要是因为 Apply.process()Processor.process() 之间的 耦合过紧,于是将其应用于 Filter 时,复用被禁止 了。

但是,如果 Processor 是一个接口(之前是一个普通的类),这些限制就会变得松动,就可以实现复用了。

classDiagram class Processor Processor <|-- StringProcessor : implements Processor <|-- FilterAdapter : implements <<interface>> Processor <<abstract>> StringProcessor Processor : Object process(Processor p, Object s) FilterAdapter : Waveform process() class StringProcessor <<abstract>> StringProcessor StringProcessor : abstract String process() StringProcessor <|-- Upcase : extends StringProcessor <|-- Downcase : extends StringProcessor <|-- Splitter : extends Upcase : String process() Downcase : String process() Splitter : String process() class Apply Apply : void process(Processor p, Object s)

按照之前的需求,我们还是想要用 Apply.process() 能够同时处理 Processor.process()Filter.process()。修改后的代码结构,既可以让 Processor 应用于 StringProcessor 也可以应用于 FilterAdapter,而后者,是继承无法办到的。这实现了 Processor.process()StringProcessor.process() 的解耦。

从这个解决方案可以看出,没有什么是加一层是不能解决的Apply.process() 方法接受 Processor 类型的对象,并不接受 Filter 类型的对象,那么我们将 Processor 作为接口,FilterAdapter 也来实现这个接口,然后由 FilterAdapter 接受 Filter 类型的对象,问题就解决了。

再举一个关于解耦的例子。 Scanner 类的构造器接受一个 Readable 接口。 Readable 是专门为 Scanner 类设计的接口。 Scanner 不必将参数限定为某个特定类,只要某个类实现了 Readable 接口就可以作为 Scanner 的参数,这就很好地体现了可扩展性和完全解耦。

//: interfaces/RandomWords.java
// Implementing an interface to conform to a method.
import java.nio.*;
import java.util.*;

public class RandomWords implements Readable {
    private static Random rand = new Random(47);
    private static final char[] capitals = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
    private static final char[] lowers = "abcdefghijklmnopqrstuvwxyz".toCharArray();
    private static final char[] vowels = "aeiou".toCharArray();
    private int count;
    public RandomWords(int count) { this.count = count; }
    public int read(CharBuffer cb) {        // 实现 Readable 接口需要重写 read() 方法
        if(count-- == 0)
            return -1; // Indicates end of input
        cb.append(capitals[rand.nextInt(capitals.length)]);
        for(int i = 0; i < 4; i++) {
            cb.append(vowels[rand.nextInt(vowels.length)]);
            cb.append(lowers[rand.nextInt(lowers.length)]);
        }
        cb.append(" ");
        return 10; // Number of characters appended
    }
    public static void main(String[] args) {
        Scanner s = new Scanner(new RandomWords(10));
        while(s.hasNext())
            System.out.println(s.next());
    }
} /* Output:
Yazeruyac
Fowenucor
Goeazimom
Raeuuacio
Nuoadesiw
Hageaikux
Ruqicibui
Numasetih
Kuuuuozog
Waqizeyoy
*///:~

嵌套接口#

接口可以嵌套在类或其他接口中。需要注意的是,当实现某个接口时,不需要实现 嵌套在其内部的任何接口,而且 private 接口不能在定义它的类之外被实现。

//: interfaces/nesting/NestingInterfaces.java
package interfaces.nesting;

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 class CImp2 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() {}
    }
    public D getD() { return new DImp2(); }
    private D dRef;
    public void receiveD(D d) {
        dRef = d;
        dRef.f();
    }
}

interface E {
    interface G {
        void f();
    }
    // Redundant "public":
    public interface H {
        void f();
    }
    void g();
    // Cannot be private within an interface:
    //! private interface I {}
}

public class NestingInterfaces {
    public class BImp implements A.B {
        public void f() {}
    }
    class CImp implements A.C {
        public void f() {}
    }
    // Cannot implement a private interface except
    // within that interface's defining class:
    //! class DImp implements A.D {
    //!    public void f() {}
    //! }
    class EImp implements E {
        public void g() {}
    }
    class EGImp implements E.G {
        public void f() {}
    }
    class EImp2 implements E {
        public void g() {}
        class EG implements E.G {
            public void f() {}
        }
    }
    public static void main(String[] args) {
        A a = new A();
        // Can't access A.D:
        //! A.D ad = a.getD();
        // Doesn't return anything but A.D:
        //! A.DImp2 di2 = a.getD();
        // Cannot access a member of the interface:
        //! a.getD().f();
        // Only another A can do anything with getD():
        A a2 = new A();
        a2.receiveD(a.getD());
    }
} ///:~

接口与工厂模式#

工厂模式是利用工厂批量生产对象的方式。理论上,通过这种方式,我们的代码将完全与接口的实现分离。 这就使得我们可以透明地将某个实现替换为另一个实现。 另外一种更优雅的方式创建工厂是使用 匿名内部类。 创建这种额外的间接性一个常见的原因是想要创建框架。

为了更深刻地理解工厂模式,我们还是用一段代码来理解一下。

classDiagram class Service class ServiceFactory <<interface>> Service <<interface>> ServiceFactory Service <|-- Implementation1 : implements Service <|-- Implementation2 : implements ServiceFactory <|-- Implementation1Factory : implements ServiceFactory <|-- Implementation2Factory : implements Service : method1() Service : method2() ServiceFactory : getService() Implementation1Factory : getService() Implementation2Factory : getService() Implementation1 : method1() Implementation1 : method2() Implementation2 : method1() Implementation2 : method2()
//: interfaces/Factories.java
import static net.mindview.util.Print.*;

interface Service {
    void method1();
    void method2();
}

interface ServiceFactory {
    Service getService();
}

class Implementation1 implements Service {
    Implementation1() {} // Package access
    public void method1() {print("Implementation1 method1");}
    public void method2() {print("Implementation1 method2");}
}

class Implementation1Factory implements ServiceFactory {
    public Service getService() {
        return new Implementation1();
    }
}

class Implementation2 implements Service {
    Implementation2() {} // Package access
    public void method1() {print("Implementation2 method1");}
    public void method2() {print("Implementation2 method2");}
}

class Implementation2Factory implements ServiceFactory {
    public Service getService() {
        return new Implementation2();
    }
}

public class Factories {
    public static void serviceConsumer(ServiceFactory fact) {
        Service s = fact.getService();
        s.method1();
        s.method2();
    }
    public static void main(String[] args) {
        serviceConsumer(new Implementation1Factory());
        // Implementations are completely interchangeable:
        serviceConsumer(new Implementation2Factory());
    }
} /* Output:
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
*///:~