Java-接口

一、抽象类和抽象方法

在[接口]https://yangandmore.github.io/2021/12/09/Java-%E5%A4%9A%E6%80%81/中的例子中。不同的子类可以用不同的方式表达此接口,以此表示所有导出类的公用部分。而另一种是将Instrument称为抽象基类。
创建抽象类是希望通过这个通用接口操作一系列类。为此Java还提供了抽象方法,以声明这个不完整的方法。

abstract void f()

二、接口

interface关键字使抽象的概念更向前进了一步。他不仅仅是一个极度抽象的类,更能完成向上转型为多种基类的类型,来实现某种类似多重继承的特性。
接口可以包含域,但是这些域隐式的是static和final的。

三、完全解偶

在面对继承的技术前,接口可以很大程度上放宽限制,因此,他可以更好的便携可复用性更高的代码。
看下面的代码:

public static String s = "ABC One Two Three";

public static void process(Processor p, Object s) {
System.out.println("Processor " + p.name());
System.out.println(p.processor(s));
}

public static void main(String[] args) {
process(new Upcase(), s);
process(new Downcase(), s);
process(new Splitter(), s);
}

static class Processor {
public String name() {
return getClass().getSimpleName();
}
Object processor(Object input) {
return input;
}
}

static class Upcase extends Processor {
String processor(Object input) {
return ((String)input).toUpperCase();
}
}

static class Downcase extends Processor {
String processor(Object input) {
return ((String)input).toLowerCase();
}
}

static class Splitter extends Processor {
String processor(Object input) {
return Arrays.toString(((String)input).split(" "));
}
}

上面很明显是以继承的方式完成操作。process()方法可以接收任何类型的Processor,并将其应用到一个Object对象,像这样创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,称为策略模式。

接下来假设发现了下面的滤波器,他适用于process()方法:

static class Waveform {
private static long counter;
private final long id = counter++;
public String toString() {
return "Waveform " + id;
}
}

static class Filter {
public String name() {
return getClass().getSimpleName();
}
public Waveform process(Waveform input) {
return input;
}
}

static class LowPass extends Filter {
double cutoff;
public LowPass(double cutoff) {
this.cutoff = cutoff;
}

public Waveform process(Waveform input) {
return input;
}
}

static class HighPass extends Filter {
double cutoff;
public HighPass(double cutoff) {
this.cutoff = cutoff;
}

public Waveform process(Waveform input) {
return input;
}
}

static class BandPass extends Filter {
double lowCutoff, highCutoff;
public BandPass(double lowCutoff, double highCutoff) {
this.lowCutoff = lowCutoff;
this.highCutoff = highCutoff;
}

public Waveform process(Waveform input) {
return input;
}
}

他们具有相同的接口元素,但是由于不是继承自Processor,因此无法使用Filter调用process()方法。

将Processor改为一个接口,这样限制就会变得松动。

public static string s = "abc one two three";

public static void process(Processor p, Object s) {
System.out.println("Processor " + p.name());
System.out.println(p.processor(s));
}

public static void main(String[] args) {
process(new Upcase(), s);
process(new Downcase(), s);
process(new Splitter(), s);
}

static interface Processor {
public String name();
Object processor(Object input);
}

static abstract class StringProcessor implements Processor {
@Override
public String name() {
return getClass().getSimpleName();
}

public abstract Object processor(Object input);
}

static class Upcase extends StringProcessor {
public String processor(Object input) {
return ((String)input).toUpperCase();
}
}

static class Downcase extends StringProcessor {
public String processor(Object input) {
return ((String)input).toLowerCase();
}
}

static class Splitter extends StringProcessor {
public String processor(Object input) {
return Arrays.toString(((String)input).split(" "));
}
}

四、Java中的多重继承

接口不仅仅是一种更纯粹的抽象类,但是他又不会向继承那样具有构造器初始化的沉重问题。这在C++中显得更加严重。

在实现了多个接口的导出类后,可以向上转型到已实现的每个接口中。

五、通过继承来扩展接口

通过继承,可以在接口中添加新的方法声明,还可以通过继承在新接口中组合数个接口。

public static void main(String[] args) {
DangerousMonster b = new DragonZilla();
u(b);
v(b);

Vampire v = new VeryBadVampire();
u(v);
v(v);
w(v);
}

static void u(Monster b) {
b.menace();
}

static void v(DangerousMonster d) {
d.menace();
d.destroy();
}

static void w(Lethal l) {
l.kill();
}

interface Monster {
void menace();
}

interface DangerousMonster extends Monster {
void destroy();
}

interface Lethal {
void kill();
}

static class DragonZilla implements DangerousMonster{
public void menace() {}
public void destroy() {}
}

interface Vampire extends DangerousMonster, Lethal {
void drinkBlood();
}

static class VeryBadVampire implements Vampire {
public void menace() {}
public void destroy() {}
public void kill() {}
public void drinkBlood() {}
}

组合接口时的命名冲突

在实现多重继承时,可能会出现命名重复的方法。

  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 void f() { }
public int f(int i) {
return 1;
}
}

class C3 extends C implements I2 {

public int f(int i) {
return 1;
}
}

class C4 extends C implements I3 {
public int f() {
return 1;
}
}
class C5 extends C implements I1 {

}

此时的问题,因为覆盖、实现、重载全都在一块儿了,并且重载方法仅通过返回类型无法区别。C5就会提示报错。

六、适配接口

接口吸引之处在于允许同一个接口具有多个不同的具体实现。接口的实现和向该方法传递的对象则取决于方法的使用者。
因此接口常见用法首先是策略模式,通过接收一个指定的接口类型,使得方法调用更加灵活。

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;
}

@Override
public int read(CharBuffer cb) throws IOException {
if (count-- == 0) return -1;
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;
}

public static void main(String[] args) {
Scanner s = new Scanner(new RandomWords(10));
while (s.hasNext()) {
System.out.printf(s.next());
}
}
}

Readable接口只需要实现read()函数,即可完成对Scanner功能是实现。

七、接口中的域

放入接口中的任何域都自动是static和final。所以接口可以成为创建常量组的工具。

初始化接口中的域

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

八、接口与工厂

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

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

static class Implementation1 implements Service {
@Override
public void method1() {
System.out.println("Implementation1 method1()");
}
@Override
public void method2() {
System.out.println("Implementation1 method2()");
}
}
static class Implementation1Factory implements ServiceFactory {

@Override
public Service getService() {
return new Implementation1();
}
}
static class Implementation2 implements Service {
@Override
public void method1() {
System.out.println("Implementation2 method1()");
}
@Override
public void method2() {
System.out.println("Implementation2 method2()");
}
}

static class Implementation2Factory implements ServiceFactory {

@Override
public Service getService() {
return new Implementation2();
}
}

public static void serviceConsumer(ServiceFactory factory) {
Service s = factory.getService();
s.method1();
s.method2();
}

public static void main(String[] args) {
serviceConsumer(new Implementation2Factory());
serviceConsumer(new Implementation1Factory());
}

为什么需要添加这种额外级别的间接性呢!因为这是在用于框架层面的使用案例。