说到接口就要说到抽象机制。可以用abstract修饰类或方法,修饰方法的时候当导出类继承这个类就要实现这个方法。这里特别提到一点,有抽象方法的类一定要是抽象类,而抽象类不一定有抽象方法(因为抽象类无法new出来,而我们有时候可能会需要这个类不能被new出来就可以这样做。)。
抽象类中可以有具体实现(也就是方法体里面写代码),而抽象接口里面只能定义方法名、参数类型和返回类型,并且没有方法体(接口内的方法只能是抽象方法,导出类必须实现这些方法),而接口里面的域默认是static跟final。
而接口跟抽象类最主要的区别就在于一个类似多重继承性质,继承extend只能继承一个基类,而一个或多个interface可以作为基类被一个导出类同时implement。也就说比较容易理解的假设,人民币extend钱(人民币is-like-a钱),而使用接口就是人民币implement钱,纸(人民币have-a钱的特性,也have-a纸的特性),假设可能不符实际只是作为一个比喻不要喷。。
完全解耦
说到完全解耦,很多人可能对这个概念是有的,但是真正说个所以然可能有点困难。下面先贴出两段代码
class Processor {
public String name() {
return getClass().getSimpleName();
}
Object process(Object input) { return input; }
}
class Upcase extends Processor {
String process(Object input) { // Covariant return
return ((String)input).toUpperCase();
}
}
public class Apply {
public static void process(Processor p, Object s) {
print("Using Processor " + p.name());
print(p.process(s));
}
}
public class Waveform {
private static long counter;
private final long id = counter++;
public String toString() { return "Waveform " + id; }
}
public class Filter {
public String name() {
return getClass().getSimpleName();
}
public Waveform process(Waveform input) { return input; }
}
public class Waveform {
private static long counter;
private final long id = counter++;
public String toString() { return "Waveform " + id; }
}
public class HighPass extends Filter {
double cutoff;
public HighPass(double cutoff) { this.cutoff = cutoff; }
public Waveform process(Waveform input) { return input; }
}
先看第一段代码框里面的Apply.process(Processor p, Object s),在这个方法里面调用p.process,然后我们看第二个框里面Filter.process,现在假设Apply.process(Processor p, Object s)里面调用的p.process,而正好Filter也有process方法并且可以在Apply.process里面进行复用,但是参数上已经固定了Processor这个参数,所以身为Filter是编译器是不允许传进去的,尽管我们人脑是觉得可以但是机器不行,那这个时候Apply.process是不是跟Processor耦合比较紧密了。先假设Apply,Filter跟它的导出类这是别人写好的,我们去调用,我们自己写的Processor跟他的导出类,我们现在把Processor跟它的导出类改成interface。
public interface Processor {
String name();
Object process(Object input);
}
class Upcase extends StringProcessor {
public String process(Object input) { // Covariant return
return ((String)input).toUpperCase();
}
}
public abstract class StringProcessor implements Processor{
public String name() {
return getClass().getSimpleName();
}
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);
}
}
好那这段代码就是跟第一段代码没啥区别,就是将继承换成接口。这个时候我们就需要到一个适配器将Filter间接的传入Apply.process(Processor p, Object s)。
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);
}
}
public class FilterProcessor {
public static void main(String[] args) {
Waveform w = new Waveform();
Apply.process(new FilterAdapter(new HighPass (1.0)), w);
}
}
输出结果:(有些人可能看到了 哎怎么是Waveform 0 不是Waveform 1,因为final修饰id,而final只能进行一次赋值就固定死了,这里id=counter++这个运算时先给id赋值counter并没有++,而counter是没有设置初始默认是0,而这个时候id=0,再进行++已经是第二次赋值了这个时候final就禁止你再次修改了,所以是Waveform 0,当然你可以把counter++改成++counter,先运算后赋值就是输出Waveform 1)
Using Processor HighPass Waveform 0
Apply.process(new FilterAdapter(new HighPass (1.0)), w);就可以间接的将Filter传入Apply.process这个方法中。就达到一个不需要修改别人代码,也能进行一个方法上的适配的作用解耦了。
然后刚开始我看到这里的时候,脑子还是有个疑问,哎,FilterAdapter implements Processor跟FilterAdapter extend Processor好像能达到同样的效果吧,那为啥要用接口呢。这里就是需要拐一个弯,把我们的想法转换一下。继承=“is-like-a”像一个,而implements就是“have-a”有一个,按照我们正常的思维,FilterAdapter这个适配器不能说是一个Processor,只能说他有Processor的特性,这样是不是更合理一些。
好了既然上面已经用一个“have-a”有一个,那么一个东西就可以拥有多种特性,假设音乐播放,看视频,充电是功能,
手机可以implements音乐播放,看视频,充电,而MP3implements音乐播放,充电,就可以进行类型多重继承进行复用,比extend单一继承又更加灵活了。
接口可以extend接口,并且可以继承多个接口,每个接口用逗号隔开,例如:interface Vampire extends DangerousMonster, Lethal,但这个用法只适用接口。但是这个用法当Vampire中有些方法名跟DangerousMonster, Lethal这两个接口中方法相同,但是可能返回类型不相同,这时候就报错,所以在接口继承继承时尽量避免方法名相同。
接口的域
,所以可以用来存常量,在java SE5之前可能会有人这样写,在阅读时需要知道这是为什么。
public interface Months {
int
JANUARY = 1, FEBRUARY = 2, MARCH = 3,
APRIL = 4, MAY = 5, JUNE = 6, JULY = 7,
AUGUST = 8, SEPTEMBER = 9, OCTOBER = 10,
NOVEMBER = 11, DECEMBER = 12;
}
也可以这样写()
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);
}
}
嵌套接口
下面贴一段代码(我已经有点烦了。。)
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();
}
}
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());
}
}
private interface D {
void f();
}接口定义成private在嵌套接口里面,但是这在output输出那块注释都是无法执行通过的, //! A.D ad = a.getD();
通俗一点就是,DImp同样是private修饰,他可以实现D接口,但是我们发现他又可以在public修饰DImp2的类中实现,但是A.DImp2只能自己调用自己,虽然接口都是public修饰,但是在嵌套里面可以使用private进行修饰,但是这样当你去new DImp2()的时候,private这个修饰符限制了只能在D中使用,所以无论你A.D ad = a.getD()向上转型还是A.DImp2 di2 = a.getD()给一个相同对象赋值,或者是a.getD().f();直接调用,都是不允许的。只能是通过自己本身的方法去调用自己本身DImp这个对象a2.receiveD(a.getD())。这是我的理解,还是有点懵不知道咋说,后面有更好的理解或者大家有自己的理解可以说一下,月不知道对不对。
接口跟工厂
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
*///:~
工厂是创建方法,而跟适配器不一样,适配器是进行转换,这个可以根据上面完全解耦时候用到的FilterAdapter进行比较,一个是创建方法,一个是转换。适配器需要定义并且的类型,而在new的时候进行构造,把自己传入方法中。那么假设有一个棋盘(书中例子),这个棋盘是一串代码Games ,而我要在这上面玩国际象棋跟跳棋,那么国际象棋跟跳棋各自有一个类实现GameFactory接口,并且返回他们各自自己的玩法实现Game这个接口,然后在Games上面根据你传入的不同游戏的工厂类CheckersFactory 或者ChessFactory ,返回相应的游戏玩法Checkers或者Chess,这样就可以做到复用这个棋盘Games,而这个Games就是当做是一个框架。Checkers,Chess实现Game所以里面的方法都是需要具体实现,而Games里面执行的代码使用到的就是Game这个类里面的方法进行编写的,所以只需要放入对应的工厂给好对应的对象,就可以正常执行。而适配器就是转换,本来Checkers,Chess就是Game的实现,他们还要转换吗。
interface Game { boolean move(); }
interface GameFactory { Game getGame(); }
class Checkers implements Game {
private int moves = 0;
private static final int MOVES = 3;
public boolean move() {
print("Checkers move " + moves);
return ++moves != MOVES;
}
}
class CheckersFactory implements GameFactory {
public Game getGame() { return new Checkers(); }
}
class Chess implements Game {
private int moves = 0;
private static final int MOVES = 4;
public boolean move() {
print("Chess move " + moves);
return ++moves != MOVES;
}
}
class ChessFactory implements GameFactory {
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());
}
} /* Output:
Checkers move 0
Checkers move 1
Checkers move 2
Chess move 0
Chess move 1
Chess move 2
Chess move 3
*///:~
总结(日常偷懒)