接口

说到接口就要说到抽象机制。可以用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
*///:~

总结(日常偷懒)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值