Thinking in Java 系列 ---(七)接口和抽象类

文章目录

  • Interfaces 接口
    • 1. 抽象类 Abstract class
    • 2. 接口Interface
        • 接口的成员变量 fields in interfaces
      • 完全解耦 Complete decoupling
      • 多重继承 “Multiple inheritance” in Java
        • 使用接口的原因:
        • 接口的继承
      • 接口和工厂模式
    • 3. 总结summary
        • 接口和抽象类的对比

Interfaces 接口

本章主要讲抽象类和接口。

1. 抽象类 Abstract class

抽象类是接口和传统类的折中。
we’ll look at the abstract class, which is a kind of midway step between an ordinary class and an interface.

抽象类是不完整的,只声明一个方法而不写方法体
abstract void f( );
如果一个类包含至少一个抽象方法,则这个类也必须抽象的。抽象的类不能创建该类的对象。当然一个抽象类中也可以没有任何抽象方法,这是为了保护这个类不可以创建该抽象类的对象。
If a class contains one or more abstract methods, the class itself must be qualified as abstract.

子类继承抽象类需要重写父类所有的抽象方法来变成正常类,否则这个子类就变成个抽象类,编译器会强制让你声明他是抽象的。

Thinking in Java 系列 ---(七)接口和抽象类_第1张图片

2. 接口Interface

接口提供了一个形式,但是没有实现,他提供了一个完全抽象的类(里面的方法都是抽象的)。
An interface provides only a form, but no implementation.
接口为所有实现他的类提供了一个“协议”,所有实现他的类必须遵守这个协议。

实现接口的类必须实现接口中的所有方法,因为接口中的方法值默认public的,所以实现的类重写的方法都必须是public。接口可以声明为default表示该接口只在该包中使用。
Thinking in Java 系列 ---(七)接口和抽象类_第2张图片

interface Instrument {
    // Compile-time constant:
    int VALUE = 5; // static & final

    // Cannot have method definitions:
    void play(Note n); // Automatically public

    void adjust();
}

class Wind implements Instrument {
    public void play(Note n) {
        print(this + ".play() " + n);
    }

    public void adjust() {
        print(this + ".adjust()");
    }

    public String toString() {
        return "Wind";
    }
}

class Percussion implements Instrument {
    public void play(Note n) {
        print(this + ".play() " + n);
    }

    public void adjust() {
        print(this + ".adjust()");
    }

    public String toString() {
        return "Percussion";
    }
}

class Stringed implements Instrument {
    public void play(Note n) {
        print(this + ".play() " + n);
    }

    public void adjust() {
        print(this + ".adjust()");
    }

    public String toString() {
        return "Stringed";
    }
}

class Brass extends Wind {
    public String toString() {
        return "Brass";
    }
}

class Woodwind extends Wind {
    public String toString() {
        return "Woodwind";
    }
}

public class Music5 {
    // Doesn't care about type, so new types added to the system still work
    // right:
    static void tune(Instrument i) {
        // ...
        i.play(Note.MIDDLE_C);
    }

    static void tuneAll(Instrument[] e) {
        for (Instrument i : e) {
            tune(i);
        }
    }

    public static void main(String[] args) {
        // Upcasting during addition to the array:
        Instrument[] orchestra = {new Wind(), new Percussion(), new Stringed(), new Brass(), new Woodwind()};
        tuneAll(orchestra);
    }
}/* Output
Wind.play() MIDDLE_C
Percussion.play() MIDDLE_C
Stringed.play() MIDDLE_C
Brass.play() MIDDLE_C
Woodwind.play() MIDDLE_C
*///:~

接口的成员变量 fields in interfaces

接口的成员变量默认为public static final的,所以他用来定义常量。如下:

public interface RandVals {
    Random RAND = new Random(47);
    int RANDOM_INT = RAND.nextInt(10);
    long RANDOM_LONG = RAND.nextLong() * 10;
    float RANDOM_FLOAT = RAND.nextLong() * 10;
    double RANDOM_DOUBLE = RAND.nextDouble() * 10;
}

//打印
public class TestMain {
    public static void main(String[] args) {
        print(RandVals.RANDOM_INT);
        print(RandVals.RANDOM_LONG);
        print(RandVals.RANDOM_FLOAT);
        print(RandVals.RANDOM_DOUBLE);
    }
}

完全解耦 Complete decoupling

举例子:我需要一个闹钟,放在我床边,每天叫我起床。但是我家里没闹钟,只有一台有闹钟功能的手机和一台有闹钟功能的洗衣机。我需要的只是闹钟功能,我不管他是什么,只要他能让我起床就好了。如果某一天我连手机都丢了,我能把洗衣机放在我床边叫我起床吗?当然可以,因为洗衣机实现了闹钟功能。所以,我们经常会这么做:把“闹钟”这个功能(而不是具体的某一项事物,如手机或者洗衣机)放在床边,如果我们想听洗衣机的闹钟声就摆洗衣机,如果想听手机的闹钟声就摆手机。
从上面的例子,我们传递的不是某个具体的对象,而是一个抽象的“闹钟功能”的概念,至于实际上传递的是什么参数,要看具体情况(取决于我想听哪一种闹钟声)。实际上,我们只关心“具有闹钟功能”这件事,我们不关心它是由谁实现的和怎样实现的,这就做到了“请求”和“实现”分离开来,这就是接口的解耦!

Decoupling interface from implementation allows an interface to be applied to multiple different implementations, and thus your code is more reusable.

One of the most compelling reasons for interfaces is to allow multiple implementations for the same interface. In simple cases this is in the form of a method that accepts an interface, leaving it up to you to implement that interface and pass your object to the method.

Thus, a common use for interfaces is the aforementioned Strategy design pattern. You write a method that performs certain operations, and that method takes an interface that you also specify. You’re basically saying, “You can use my method with any object you like, as long as your object conforms to my interface.” This makes your method more flexible, general and reusable.
接口最引人注目的原因之一是允许对同一个接口使用多个实现。在简单的情况下,这是以接受接口的方法的形式出现的,由您来实现接口并将对象传递给方法。

因此,接口的一个常见用途就是前面提到的策略设计模式。您编写一个执行特定操作的方法,该方法接受您还指定的接口。你基本上是在说,“你可以对任何你喜欢的对象使用我的方法,只要你的对象符合我的接口。”这使您的方法更加灵活、通用和可重用。

多重继承 “Multiple inheritance” in Java

java中子类不能继承多个父类,但是接口变相实现了这种功能,一个类可以实现多个接口。

interface CanFight {
    void fight();
}

interface CanSwim {
    void swim();
}

interface CanFly {
    void fly();
}

class ActionCharacter {

    public void fight() {
        System.out.println("actioner fight");
    }
}

class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly {
//从父类中继承了fight方法
    @Override
    public void swim() {
        System.out.println("hero swim");
    }

    @Override
    public void fly() {
        System.out.println("hero fly");
    }
}

public class Adventure {
    public static void t(CanFight x) {
        x.fight();
    }

    public static void u(CanSwim x) {
        x.swim();
    }

    public static void v(CanFly x) {
        x.fly();
    }

    public static void w(ActionCharacter x) {
        x.fight();
    }

    public static void main(String[] args) {


        Hero h = new Hero();
        t(h); // Treat it as a CanFight 这个方法是继承ActionCharacter所以他实现了接口中的fight方法
        u(h); // Treat it as a CanSwim
        v(h); // Treat it as a CanFly
        w(h); // Treat it as an ActionCharacter
    }
} 
/*
actioner fight
hero swim
hero fly
actioner fight
 */

使用接口的原因:

使用接口第一个原因就是,可以向上转型为任何一个接口并使用,也就是说:每个接口有他自己的功能,我创建一个类并继承这个接口那么我就拥有了这个功能,继承更多的接口就拥有更多的功能,那么我的类就更加强大,这也就是接口存在的意义。
Keep in mind that one of the core reasons for interfaces is shown in the preceding example: to upcast to more than one base type (and the flexibility that this provides).
第二个原因就是,防止客户端程序员创建我为他提供接口的类,因为接口不能创建类,所以他只是使用。
However, a second reason for using interfaces is the same as using an abstract base class: to prevent the client programmer from making an object of this class and to establish that it is only an interface.

you should always prefer interfaces to abstract classes。所以尽量用interface而不是abstract class

接口的继承

继承后的接口,就拥有了父接口的所有方法,不同的接口中方法名要避免重复。

interface Monster {
	void menace();
}

interface DangerousMnoster extends Monster {
	void destory();
}


interface Vampire extends DangerousMnoster {
	void drinkBlood();
}

class VeryBadVampire implements Vampire {
	public void menace() {
		System.out.println("menace");
	}

	public void destory() {
		System.out.println("destroy");
	}

	public void drinkBlood() {
		System.out.println("drink");
	}
}

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

	static void v(DangerousMnoster d) {
		d.menace();
		d.destory();
	}

	public static void main(String[] args) {

		Vampire vlad = new VeryBadVampire();
		u(vlad);
		v(vlad);
	}
} 
/*
menace
menace
destroy
 */

接口和工厂模式

举例说明,比如一个Shape接口,然后我用Circle,Squaare等类来实现这个接口,我可以创建一个ShapeFactory工厂,来获取每个不同类的对象,只需要知道这个对象的名称就可以了。相当于这是一个Shape的工厂,我不需要知道如何new一个圆形,而我只需要调用工厂方法Shape shape1 = shapeFactory.getShape("CIRCLE");就可以得到这个圆形对象。所有的创建对象在工厂中完成,我们只需要从工厂中提货就可以了。
这样非常容易扩展,如果想增加一个图形,只需要在工厂里面添加就可以了。
具体实现非常简明的教程http://www.runoob.com/design-pattern/factory-pattern.html

3. 总结summary

一个适当的指导方针是在设计时更倾向选择类而不是接口。从类开始,如果很明显需要接口,那么重构时使用接口。接口是一个很好的工具,但是它们很容易被过度使用。
An appropriate guideline is to prefer classes to interfaces. Start with classes, and if it becomes clear that interfaces are necessary, then refactor. Interfaces are a great tool, but they can easily be overused.

接口和抽象类的对比

Thinking in Java 系列 ---(七)接口和抽象类_第3张图片

你可能感兴趣的:(Thinking,in,Java,Thinking,in,Java,读书笔记)