文章目录
序言
- 接口和抽象类提供了将接口和实现分离的结构化方式
1. 抽象类和方法
- 构建通用接口的唯一理由是为了让不同的子类有不同的表达。
- 抽象基类即抽象类。
- 抽象类的对象几乎没有含义。创建抽象类的目的是为了通过通用接口操作一组类。
- java提供抽象类检查机制:抽象方法(abstract method)
- 包含抽象方法的类称为抽象类。用abstract限定。
- 构建抽象类的对象编译器会报错。
- 如果从抽象类继承并用子类创建对象,必须要定义所有抽象基类的抽象方法。如果不这样做。则子类也是抽象类。
代码示例:
package interfaces.music;
import polymorphism.music.Note;
import static net.mindview.util.Print.print;
/**
* @author vincient
* @create 2020-04-15 10:27 PM
*/
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 {
@Override
public void play(Note n) {
print("Wind.play() " + n);
}
@Override
public String what() {
return "Wind";
}
@Override
public void adjust() {
}
}
class Percussion extends Instrument {
@Override
public void play(Note n) {
print("Percussion.play() " + n);
}
@Override
public String what() {
return "Percussion";
}
@Override
public void adjust() {
}
}
class Stringed extends Instrument {
@Override
public void play(Note n) {
print("Stringed.play() " + n);
}
@Override
public String what() {
return "Stringed";
}
@Override
public void adjust() {
}
}
class Brass extends Wind {
@Override
public void play(Note n) {
print("Brass.play() " + n);
}
@Override
public void adjust() {
print("Brass.adjust()");
}
}
class Woodwind extends Wind {
@Override
public void play(Note n) {
print("Woodwind.play() " + n);
}
@Override
public String what() {
return "Woodwind";
}
}
public class Music4 {
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) {
Instrument[] orchestra = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
}
}
// 运行结果
// Wind.play() MIDDLE_C
// Percussion.play() MIDDLE_C
// Stringed.play() MIDDLE_C
// Brass.play() MIDDLE_C
// Woodwind.play() MIDDLE_C
- 抽象类是很有用的重构工具,用于将公共方法沿继承层次结构上移。
2. 接口
- interface关键词将抽象的概念更近了一步。abstract允许在类中创建一个或多个未定义的方法。interface提供了完全的抽象类,根本不提供任何实现。接口仅仅提供形式,不提供实现。
- 接口的意思是说:所有实现了该接口的类都看起来像这个接口。任何使用某特定接口的代码都知道可以调用哪些方法。因此接口被用来建立类之间的协议。
- 接口不仅仅是一个极度抽象的类,他允许人们实现多重继承的变体,通过创建一个可以被向上转型为多种基本类型。
- 使用interface代替class创建接口。接口的字段是隐式static和final的。
- 使用implements让类遵循某个接口或一组接口。看起来像继承。
- 一旦实现了某个接口,实现就会成为一个普通的类,可以按常规方式扩展。
- 接口中的方法是public的,实现时需要定义为public,否则就是包访问权限的。
代码示例:
package interfaces.music5;
import polymorphism.music.Note;
import static net.mindview.util.Print.print;
/**
* @author vincient
* @create 2020-04-18 11:08 AM
*/
interface Instrument {
int VALUE = 5;
void play(Note n);
void adjust();
}
class Wind implements Instrument {
@Override
public void play(Note n) {
print(this + ".play() " + n);
}
@Override
public void adjust() {
print(this + ".adjust()");
}
@Override
public String toString() {
return "Wind";
}
}
class Percussion implements Instrument {
@Override
public void play(Note n) {
print(this + ".play() " + n);
}
@Override
public void adjust() {
print(this + ".adjust()");
}
@Override
public String toString() {
return "Percussion";
}
}
class Stringed implements Instrument {
@Override
public void play(Note n) {
print(this + ".play() " + n);
}
@Override
public void adjust() {
print(this + ".adjust()");
}
@Override
public String toString() {
return "Stringed";
}
}
class Brass extends Wind {
@Override
public String toString() {
return "Brass";
}
}
class Woodwind extends Wind {
@Override
public String toString() {
return "Woodwind";
}
}
public class Music5 {
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) {
Instrument[] orchestra = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
}
}
// 运行结果
// Wind.play() MIDDLE_C
// Percussion.play() MIDDLE_C
// Stringed.play() MIDDLE_C
// Brass.play() MIDDLE_C
// Woodwind.play() MIDDLE_C
3. 完全解耦
- 接口可以保证复用性更好的代码的编写。
代码示例:
package interfaces.classprocessor;
import java.util.Arrays;
import static net.mindview.util.Print.print;
/**
* @author vincient
* @create 2020-04-18 12:18 PM
*/
class Processor {
public String name() {
return getClass().getSimpleName();
}
Object process(Object input) {
return input;
}
}
class Upcase extends Processor {
@Override
String process(Object input) {
return ((String) input).toUpperCase();
}
}
class Downcase extends Processor {
@Override
String process(Object input) {
return ((String) input).toLowerCase();
}
}
class Splitter extends Processor {
@Override
String process(Object input) {
return Arrays.toString(((String) input).split(" "));
}
}
public class Apply {
public static void process(Processor p, Object s) {
print("Using Processor " + p.name());
print(p.process(s));
}
public 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);
}
}
// 运行结果
// Using Processor Upcase
// DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT
// Using Processor Downcase
// disagreement with beliefs is by definition incorrect
// Using Processor Splitter
// [Disagreement, with, beliefs, is, by, definition, incorrect]
- 策略设计模式:创建一个方法,根据传递给它的参数对象的不同而表现出不同的行为。
代码示例:
package interfaces.interfaceprocessor;
import java.util.Arrays;
/**
* @author vincient
* @create 2020-04-20 8:53 AM
*/
public abstract class StringProcessor implements Processor {
@Override
public String name() {
return getClass().getSimpleName();
}
@Override
public abstract String process(Object input);
public static String s = "If she weighs the same as a duck, she’s made of wood";
public static void main(String[] args) {
Apply.process(new UpCase(), s);
Apply.process(new DownCase(), s);
Apply.process(new SpLitter(), s);
}
}
class UpCase extends StringProcessor {
@Override
public String process(Object input) {
return ((String) input).toUpperCase();
}
}
class DownCase extends StringProcessor {
@Override
public String process(Object input) {
return ((String) input).toLowerCase();
}
}
class SpLitter extends StringProcessor{
@Override
public String process(Object input) {
return Arrays.toString(((String)input).split(" "));
}
}
// 运行结果
// Using processor UpCase
// IF SHE WEIGHS THE SAME AS A DUCK, SHE’S MADE OF WOOD
// Using processor DownCase
// if she weighs the same as a duck, she’s made of wood
// Using processor SpLitter
// [If, she, weighs, the, same, as, a, duck,, she’s, made, of, wood]
- 适配器设计模式:接受已有的接口,生成需要的接口。
代码示例:
package interfaces.interfaceprocessor;
import interfaces.filters.*;
/**
* @author vincient
* @create 2020-04-20 9:06 AM
*/
class FilterAdapter implements Processor {
Filter filter;
public FilterAdapter(Filter filter) {
this.filter = filter;
}
@Override
public String name() {
return filter.name();
}
@Override
public Object process(Object input) {
return filter.process((Waveform) input);
}
}
public class FilterProcessor {
public static void main(String[] args) {
Waveform w = new Waveform();
Apply.process(new FilterAdapter(new LowPass(1.0)), w);
Apply.process(new FilterAdapter(new HighPass(2.0)), w);
Apply.process(new FilterAdapter(new BandPass(3.0, 4.0)), w);
}
}
// 运行结果
// Using processor LowPass
// waveform 0
// Using processor HighPass
// waveform 0
// Using processor BandPass
// waveform 0
4. Java多重继承
- 接口没有实现,无法阻止多个接口组合。
- 在导出类中,不必拥有抽象或具体的基类。只能继承一个非接口,其余的基本部分必须是接口。
代码示例:
package interfaces;
/**
* @author vincient
* @create 2020-04-20 12:56 PM
*/
interface CanFight {
void fight();
}
interface CanSwim {
void swim();
}
interface CanFly {
void fly();
}
class ActionCharacter {
public void fight() {
}
}
class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly {
@Override
public void swim() {
}
@Override
public void 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
u(h); // Treat it as a CanSwim
v(h); // Treat it as a CanFly
w(h); // Treat it as an ActionCharacter
}
}
- 创建对象时,所有定义都必须先存在。
- 应该使用接口还是抽象类:如果知道某东西会成为基类,可以考虑把他做成一个接口。
5. 通过继承扩展接口
代码示例:
package interfaces;
/**
* @author vincient
* @create 2020-04-20 1:41 PM
*/
interface Monster {
void menace();
}
interface DangerousMonster extends Monster {
void destroy();
}
interface Lethal {
void kill();
}
class DragonZilla implements DangerousMonster {
@Override
public void menace() {
}
@Override
public void destroy() {
}
}
interface Vampire extends DangerousMonster, Lethal {
void drinkBlood();
}
class VeryBadVampire implements Vampire {
@Override
public void menace() {
}
@Override
public void destroy() {
}
@Override
public void kill() {
}
@Override
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);
}
}
- 唯独在继承多个基本接口时,可以使用extend,接口和接口之间有逗号相隔。
5.1 组合接口时的命名冲突
代码示例:
package interfaces;
/**
* @author vincient
* @create 2020-04-20 5:34 PM
*/
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{
@Override
public void f() {
}
@Override
public int f(int i) {
return 1;
}
}
class C3 extends C implements I2{
@Override
public int f(int i) {
return 1;
}
}
class C4 extends C implements I3{
public int f(){
return 1;
}
}
public class InterfaceCollision {
}
结论:在要组合的不同接口中使用相同的方法名称通常也会导致代码的可读性混乱。
6. 适配接口
- 接口最吸引人的地方之一是允许对同一接口的多种实现。
- 接口的常见应用场景是策略设计模式。
代码示例:
package interfaces;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Random;
import java.util.Scanner;
/**
* @author vincient
* @create 2020-04-21 9:03 AM
*/
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.println(s.next());
}
}
}
// 运行结果
// Yazeruyac
// Fowenucor
// Goeazimom
// Raeuuacio
// Nuoadesiw
// Hageaikux
// Ruqicibui
// Numasetih
// Kuuuuozog
// Waqizeyoy
package interfaces;
import java.util.Random;
/**
* @author vincient
* @create 2020-04-21 9:19 AM
*/
public class RandomDoubles {
private static Random rand = new Random(47);
public double next() {
return rand.nextDouble();
}
public static void main(String[] args) {
RandomDoubles rd = new RandomDoubles();
for (int i = 0; i < 7; i++) {
System.out.print(rd.next() + " ");
}
}
}
// 运行结果
// 0.7271157860730044 0.5309454508634242 0.16020656493302599 0.18847866977771732 0.5166020801268457 0.2678662084200585 0.2613610344283964
package interfaces;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Scanner;
/**
* @author vincient
* @create 2020-04-21 9:32 AM
*/
public class AdaptedRandomDoubles extends RandomDoubles implements Readable {
private int count;
public AdaptedRandomDoubles(int count) {
this.count = count;
}
@Override
public int read(CharBuffer cb) throws IOException {
if (count-- == 0) {
return -1;
}
String result = Double.toString(next()) + " ";
cb.append(result);
return result.length();
}
public static void main(String[] args) {
Scanner s = new Scanner(new AdaptedRandomDoubles(7));
while (s.hasNext()) {
System.out.print(s.nextDouble() + " ");
}
}
}
// 运行结果
// 0.7271157860730044 0.5309454508634242 0.16020656493302599 0.18847866977771732 0.5166020801268457 0.2678662084200585 0.2613610344283964
结论:可以将接口加于任何现有的类之上,意味着一个采取接口的方法为任何类提供了一种使之可以与该方法相适应的方式。
7. 接口中的字段
- 接口中任何字段都是自动static和final的,使得接口成为了一种用来创建常量组的便捷工具。
7.1 初始化接口中的字段
- 接口中的字段不可以被定义为“blank finals”,但是可以被非常量表达式初始化。
代码示例:
package interfaces;
import java.util.Random;
/**
* @author vincient
* @create 2020-04-21 10:12 AM
*/
public interface RandVals {
Random RAND = new Random(47);
int RANDOM_INT = RAND.nextInt(10);
long RANDOM_LONG = RAND.nextLong() * 10;
float RANDOM_FLOAT = RAND.nextFloat() * 10;
double RANDOM_DOUBLE = RAND.nextDouble() * 10;
}
- 字段在类第一次被加载时初始化,发生于任何字段第一次被访问。
代码示例
package interfaces;
import static net.mindview.util.Print.print;
/**
* @author vincient
* @create 2020-04-21 10:16 AM
*/
public class TestRandVals {
public static void main(String[] args) {
print(RandVals.RANDOM_INT);
print(RandVals.RANDOM_LONG);
print(RandVals.RANDOM_FLOAT);
print(RandVals.RANDOM_DOUBLE);
}
}
// 运行结果
// 8
// -32032247016559954
// -8.5939291E18
// 5.779976127815049
- 字段并不是接口的一部分。这些值被存储在该接口的静态存储区中。
8. 接口嵌套
- 接口可以被嵌套在类和接口中。
代码示例:
package interfaces.nesting;
/**
* @author vincient
* @create 2020-04-21 10:26 AM
*/
class A {
interface B{
void f();
}
public class BImp implements B{
@Override
public void f() {
}
}
public class BImp2 implements B{
@Override
public void f() {
}
}
public interface C{
void f();
}
class CImp implements C{
@Override
public void f() {
}
}
private class CImp2 implements C{
@Override
public void f() {
}
}
private interface D{
void f();
}
private class DImp implements D{
@Override
public void f() {
}
}
public class DImp2 implements D{
@Override
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();
}
interface H{
void f();
}
void g();
}
public class NestingInterfaces {
public class BImp implements A.B{
@Override
public void f() {
}
}
class CImp implements A.C{
@Override
public void f() {
}
}
class EImp implements E{
@Override
public void g() {
}
}
class EGImp implements E.G{
@Override
public void f() {
}
}
class EImp2 implements E{
@Override
public void g() {
}
class EG implements E.G{
@Override
public void f() {
}
}
}
public static void main(String[] args) {
A a = new A();
A a2 = new A();
a2.receiveD(a.getD());
}
}
- 嵌套接口拥有public和package两种可视性。
- 接口可是被设置为private(嵌套接口和嵌套类一致)。
- private嵌套接口可以被实现为private内部类,也可以被实现为public类。
- 接口中的嵌套接口必须是public的。
- 实现接口时不需要实现嵌套接口。
9. 接口和工厂
- 接口的目的是作为多个实现的入口,而产生符合接口的对象的典型方式就是Factory Method设计模式
代码示例:
package interfaces;
import static net.mindview.util.Print.print;
/**
* @author vincient
* @create 2020-04-21 3:52 PM
*/
interface Service {
void method1();
void method2();
}
interface ServiceFactory {
Service getService();
}
class Implementation1 implements Service {
Implementation1() {
}
@Override
public void method1() {
print("Implementation1 method1");
}
@Override
public void method2() {
print("Implementation1 method2");
}
}
class Implementation1Factory implements ServiceFactory {
@Override
public Service getService() {
return new Implementation1();
}
}
class Implementation2 implements Service {
Implementation2() {
}
@Override
public void method1() {
print("Implementation2 method1");
}
@Override
public void method2() {
print("Implementation2 method2");
}
}
class Implementation2Factory implements ServiceFactory {
@Override
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());
serviceConsumer(new Implementation2Factory());
}
}
// 运行结果
// Implementation1 method1
// Implementation1 method2
// Implementation2 method1
// Implementation2 method2
代码示例:
package interfaces;
import static net.mindview.util.Print.print;
/**
* @author vincient
* @create 2020-04-21 4:07 PM
*/
interface Game {
boolean move();
}
interface GameFactory {
Game getGame();
}
class Checkers implements Game {
private int moves = 0;
private static final int MOVES = 3;
@Override
public boolean move() {
print("Checkers move " + moves);
return ++moves != MOVES;
}
}
class CheckersFactory implements GameFactory {
@Override
public Game getGame() {
return new Checkers();
}
}
class Chess implements Game {
private int moves = 0;
private static final int MOVES = 4;
@Override
public boolean move() {
print("Chess move " + moves);
return ++moves != MOVES;
}
}
class ChessFactory implements GameFactory {
@Override
public Game getGame() {
return new Chess();
}
}
public class Games {
public static void playGame(GameFactory factory) {
Game s = factory.getGame();
while (s.move()) {
}
}
public static void main(String[] args) {
playGame(new CheckersFactory());
playGame(new ChessFactory());
}
}
// 运行结果
// Checkers move 0
// Checkers move 1
// Checkers move 2
// Chess move 0
// Chess move 1
// Chess move 2
// Chess move 3
10. 结论
- 优先考虑类,而不是接口。先从类开始,如果很明确需要接口,那么就重构。