• TJI读书笔记12-接口


     

     

    接口和抽象类为我们提供了更强又有力的接口和实现分离的方法.

    抽象类和抽象方法

    抽象类的目的是为了给子类提供一个通用接口,通过这样一个通用的接口,可以操纵一系列的子类. 也就是说,只需要通过相同的接口调用就可以操作不用的实现. 这也就是所谓的接口和实现的分离.

    抽象类简单来说就是包含抽象方法的类. 那么,要了解抽象类就得先了解抽象方法. abstract void f();像这样的由abstract修饰的方法叫做抽象方法. 抽象方法只有返回值类型和方法签名,没有方法体. 抽象类同样适用abstract修饰,并且抽象类不能实例化.

    1.abstract class Instrument{
    2.
    3. abstract public void paly(Note m);
    4. private void test1(){
    5. System.out.println("Instrument");
    6. }
    7.}
    8.class Wind extends Instrument{
    9. public void paly(Note m){
    10. System.out.println("Wind play()");
    11. }
    12.
    13. public void test1(){
    14. System.out.println("111");
    15. }
    16.}
    17.
    18.public class Music {
    19. public static void tune(Instrument i){
    20. i.paly(Note.C_SHARP);
    21. }
    22. public static void main(String[] args) {
    23. Wind flute = new Wind();
    24. tune(flute);
    25. }
    26.}

    抽象类作为基类,对子类中需要改变的方法仅提供abstract方法,对于其他方法,可以正常提供方法体,算是一种介于接口和正常类之间的一种中庸的方案. 抽象类使类的抽象性明确起来了. 抽象类是很重要的重构工具,因为它可以容易的将公共方法沿着继承层次结构向上移动.(埃大爷说的好学院范儿…)

    接口

    如果一个抽象类中的方法都是抽象方法,那么它的作用就更抽象了. 这么一种纯粹的抽象类,它只提供一个类型的接口定义,没有任何实现. 那么这么一种抽象的令人发指的抽象类如果不给它定义成一个新的概念简直天理难容. 于是这种玩意儿就叫做接口. 它只提供一个类的接口定义于是就被粗暴的叫成了接口… 注意这个接口是java的一种类型机制,平时所说的类的接口是指类中暴露出的可以使用的方法. 可以说接口是用用来被建立类和类之间的协议. (有些语言会直接使用protocol完成这一功能)

    interface不仅仅是一个纯粹的抽象类,它允许人们通过创建一个能够被向上转型的多种基类的类型. 除了这个,interface还有一个功能就是可以实现类似多继承的功能. 需要注意的几个点:

    • 接口中定义的方法,隐式添加 public static final
    • 接口中可以定义成员变量,默认都是static final的
    • 要让一个类实现一个接口需要使用implements关键字
    • 实现一个接口的类必须要实现这个接口中定义的所有方法.
    • 实现一个接口的类,可以定义自己的public方法. 也就是说实现一个interface的类对外提供的接口是所实现interface的子集.
    1.interface Instrument2{
    2. int Value = 5;
    3. void paly(Note n);
    4. void adjust();
    5.}
    6.
    7.public class Wind2 implements Instrument2{
    8. public void paly(Note n ){
    9. System.out.println(this+".play() "+n);
    10. }
    11. public void adjust(){
    12. System.out.println(this+".adjust()");
    13. }
    14. public String toString(){
    15. return "Wind";
    16. }
    17. public void foo1(){
    18. System.out.println("hoho");
    19. }
    20.
    21. public static void main(String[] args) {
    22. Wind2 wind = new Wind2();
    23. wind.adjust();
    24. wind.paly(Note.B_FLAT);
    25. wind.foo1();
    26. System.out.println(Instrument2.Value);
    27. }
    28.}

    完全解耦和策略模式

    1.import java.util.Arrays;
    2.
    3.class Processor{
    4. public String name(){
    5. return getClass().getSimpleName();
    6. }
    7. Object process(Object input){
    8. return input;
    9. }
    10.}
    11.
    12.class UpCase extends Processor{
    13. String process(Object input){
    14. return ((String)input).toUpperCase();
    15. }
    16.}
    17.
    18.class LowerCase extends Processor{
    19. String process(Object input){
    20. return ((String)input).toLowerCase();
    21. }
    22.}
    23.
    24.class Splitter extends Processor{
    25. String process(Object input){
    26. return Arrays.toString(((String)input).split(" "));
    27. }
    28.}
    29.
    30.public class Apply {
    31. public static void process(Processor p,Object s){
    32. System.out.println("using Processor "+p.name());
    33. System.out.println(p.process(s));
    34. }
    35. public static String s = "hello world";
    36. public static void main(String[] args) {
    37. Apply.process(new UpCase(), s);
    38. Apply.process(new LowerCase(), s);
    39. Apply.process(new Splitter(), s);
    40. }
    41.}

    上面是一个经典的策略模式,同一个方法,根据入参不同会有不同的行为. 所谓见人说人话,见鬼说鬼话. 注意实现方式,本质其实就是多态. 策略就是传入的参数的对象.它包含所要执行的代码.

    但是看下面这个例子

    1.public class Waveform {
    2. private static long counter;
    3. private final long id = counter++;
    4. public String toString(){
    5. return "Waveform "+id;
    6. }
    7.}
    8.//=========================================================
    9.public class Filter {
    10. public String name(){
    11. return getClass().getSimpleName();
    12. }
    13. public Waveform process(Waveform input){
    14. return input;
    15. }
    16.}
    17.//=========================================================
    18.public class LowPass extends Filter{
    19. double cutoff;
    20.
    21. public LowPass(double cutoff){
    22. this.cutoff=cutoff;
    23. }
    24. public Waveform process(Waveform input){
    25. return input;
    26. }
    27.}
    28.//=========================================================
    29.public class HighPass extends Filter{
    30. double cutoff;
    31.
    32. public HighPass(double cutoff) {
    33. this.cutoff = cutoff;
    34. }
    35. public Waveform process(Waveform input) {
    36. return input;
    37. }
    38.}
    39.//=========================================================
    40.public class BandPass extends Filter{
    41. double lowCutoff,HighCutoff;
    42.
    43. public BandPass(double lowCutoff,double HighCutoff){
    44. this.HighCutoff=HighCutoff;
    45. this.lowCutoff=lowCutoff;
    46. }
    47. public Waveform process(Waveform input){
    48. return input;
    49. }
    50.}
    51.

    Alt text

    .1473781578401

    上面这段代码就会有个问题,Filter和Processor虽然有相同的接口元素,但是他并非继承自Processor. 因为Filter的创建者可能并不知道你想将其用作processor. 这样就不能通过Apply.process()来调用了. 虽然也有办法可以正常运行,但是processor和Apply.process()之间的耦合太紧. 想复用Apply.process()的时候就出现问题. 所以有个办法就是把processor抽象为一个接口,然后处理不同问题的时候,再去实现这个接口.

    1.public interface Processor{
    2. public String name();
    3. public Object process(Object input);
    4.}
    5.//=========================================================
    6.import java.util.Arrays;
    7.public abstract class StringProcessor implements Processor {
    8. public String name() {
    9. return getClass().getSimpleName();
    10. }
    11.
    12. public abstract String process(Object input);
    13.
    14. public static String s = "hello world";
    15.
    16. public static void main(String[] args) {
    17. Apply.process(new UpCase(), s);
    18. Apply.process(new LowerCase(), s);
    19. Apply.process(new Splitter(), s);
    20. }
    21.}
    22.
    23.class UpCase extends StringProcessor {
    24.
    25. public String process(Object input) {
    26. return ((String) input).toUpperCase();
    27. }
    28.
    29.}
    30.
    31.class LowerCase extends StringProcessor {
    32. public String process(Object input) {
    33. return ((String) input).toLowerCase();
    34. }
    35.}
    36.
    37.class Splitter extends StringProcessor {
    38. public String process(Object input) {
    39. return Arrays.toString(((String) input).split(" "));
    40. }
    41.}
    42.//=========================================================
    43.public abstract class FilterProcessor implements Processor{
    44.
    45. @Override
    46. public String name() {
    47.
    48. return getClass().getSimpleName();
    49. }
    50. @Override
    51. abstract public Waveform process(Object input);
    52.
    53. public static Waveform w = new Waveform();
    54. public static void main(String[] args) {
    55. Apply.process(new HighPass(1222.222), w);
    56. Apply.process(new LowPass(1222.222), w);
    57. Apply.process(new BandPass(1222.222,3333.3333), w);
    58. }
    59.}
    60.//=========================================================
    61.public class HighPass extends FilterProcessor{
    62.
    63. double cutoff;
    64.
    65. public HighPass(double cutoff) {
    66. this.cutoff = cutoff;
    67. }
    68. @Override
    69. public Waveform process(Object input) {
    70. return (Waveform)input;
    71. }
    72.}
    73.class LowPass extends FilterProcessor{
    74.
    75. double cutoff;
    76.
    77. public LowPass(double cutoff){
    78. this.cutoff=cutoff;
    79. }
    80. @Override
    81. public Waveform process(Object input){
    82. return (Waveform)input;
    83. }
    84.}
    85.class BandPass extends FilterProcessor{
    86. double lowCutoff,HighCutoff;
    87.
    88. public BandPass(double lowCutoff,double HighCutoff){
    89. this.HighCutoff=HighCutoff;
    90. this.lowCutoff=lowCutoff;
    91. }
    92. @Override
    93. public Waveform process(Object input) {
    94. return (Waveform)input;
    95. }
    96.}

    .1473783687978

    如果有时候碰到了一些你想使用,但是又不能修改的类. 那么就可以使用一种新的模式,适配器模式. 适配器中的代码将接受你所拥有的接口,并且产生你需要的接口.

    1.class FilterAdapter implements Processor{
    2. Filter filter;
    3. public FilterAdapter(Filter filter){
    4. this.filter= filter;
    5. }
    6. @Override
    7. public String name(){
    8. return filter.name();
    9. }
    10. @Override
    11. public Waveform process(Object input){
    12. return filter.process((Waveform)input);
    13. }
    14.}
    15.
    16.public class NewFilterProcessor{
    17. public static void main(String[] args) {
    18. Waveform w = new Waveform();
    19. Apply.process(new FilterAdapter(new LowPass(1.0)), w);
    20. Apply.process(new FilterAdapter(new HighPass(2.0)), w);
    21. Apply.process(new FilterAdapter(new BandPass(1.0,2.0)), w);}
    22.}

    .1473783687978
    (这个生成UML的插件貌似有bug,还是我对UML的理解有bug,总感觉哪里不对劲…)

    像这个样子,FilterAdapter接受processor的接口,然后生成我们需要的process()接口.这是应该是适配器模式的重要作用之一.
    还有一个用途, 比如ContainerAdapter这个类,实现了ContainerListener接口,但是所有方法都是空并未提供实际的方法代码.那这种Adapter的作用很巧妙,这样我们只需要继承这个类,就等于实现了这个接口,于此同时可以只实现自己感兴趣的方法. 而不像直接实现接口那样即使只对其中部分方法感兴趣也要实现所有的方法.

    interface从具体实现中解耦,使得接口可以应用于多种不同的具体实现,因此代码就更具有可复用性.

    java中的多继承
    C++中允许多重继承,即一个子类可以有多个基类.这样会造成一些混淆. java中禁止了这种方式的多重继承. java中的类,一个子类只能有一个基类,但是是可以实现多个接口的.

    接口间的继承关系

    接口之间是可以存在继承关系的,通过继承,可以扩展一个interface的所拥有的接口.

    1.interface Monster {
    2. void menace();
    3.}
    4.
    5.interface DanderousMonster extends Monster {
    6. void destroy();
    7.}
    8.
    9.interface Lethal {
    10. void kill();
    11.}
    12.
    13.class DragonZilla implements DanderousMonster {
    14.
    15. @Override
    16. public void menace() {
    17. // TODO Auto-generated method stub
    18.
    19. }
    20.
    21. @Override
    22. public void destroy() {
    23. // TODO Auto-generated method stub
    24.
    25. }
    26.
    27.}
    28.
    29.interface Vampire extends DanderousMonster, Lethal {
    30. void drinkBlood();
    31.}
    32.
    33.class VeryBadVampire implements Vampire {
    34.
    35. @Override
    36. public void destroy() {
    37. // TODO Auto-generated method stub
    38.
    39. }
    40.
    41. @Override
    42. public void menace() {
    43. // TODO Auto-generated method stub
    44.
    45. }
    46.
    47. @Override
    48. public void kill() {
    49. // TODO Auto-generated method stub
    50.
    51. }
    52.
    53. @Override
    54. public void drinkBlood() {
    55. // TODO Auto-generated method stub
    56.
    57. }
    58.
    59.}

    .1473855344432

    组合接口时的名字冲突问题
    这是一个可能会遇到的问题, 不同的接口中的两个方法,方法名一样但是,签名或者返回类型不同. 那这种时候会发生什么事情呢?
    看一段让人心碎的代码

    1.interface I1{
    2. public void f();
    3.}
    4.interface I2{
    5. public int f(int i);
    6.}
    7.interface I3{
    8. public int f();
    9.}
    10.class C{
    11. public int f(){
    12. return 2;
    13. }
    14.}
    15.
    16.
    17.class C2 implements I1,I2{
    18.
    19. @Override
    20. public int f(int i) {
    21. return 0;
    22. }
    23.
    24. @Override
    25. public void f() {
    26. }
    27.}
    28.
    29.class C3 extends C implements I2{
    30.
    31. @Override
    32. public int f(int i) {
    33. return 0;
    34. }
    35.
    36.}
    37.
    38.class C4 extends C implements I3{
    39. @Override
    40. public int f(){
    41. return 3;
    42. }
    43.}
    44.
    45.//The return types are incompatible for the inherited methods I1.f(), C.f()
    46.class C5 extends C implements I1{
    47.
    48.}

    其实从最简单的角度去理解,interface实现,类继承,反正无论多少东西混到一起,最终的作用效果就是,构建一个类的接口(方法).也就是说所有的方法最终都是要进到一个类中去 所以,方法名相同的时候,可不可以的衡量标准其实就是方法能不能重载.

    但是,组合不同接口的时候,方法名相同可能会造成代码的可读性下降. 最好还是不要这么搞.

    工厂模式

    工厂方法模式与直接调用构造器不同. 我们在工厂对象上调用创建的方法. 这样做的好处是,代码完全与接口实现分离. 像下面这段代码中. 要创建不同的对象,只需要调用同一个方法Factories.serviceCustomer()方法即可.
    工厂模式在创建框架的过程中使用的最多.

    1.interface Service{
    2. void method1();
    3. void method2();
    4.}
    5.interface ServiceFactory{
    6. Service getService();
    7.}
    8.
    9.class Implementation1 implements Service{
    10.
    11. @Override
    12. public void method1() {
    13. System.out.println("111:method1");
    14. }
    15.
    16. @Override
    17. public void method2() {
    18. System.out.println("111:method2");
    19. }
    20.}
    21.
    22.class Implementation2 implements Service{
    23.
    24. @Override
    25. public void method1() {
    26. System.out.println("222:method1");
    27. }
    28.
    29. @Override
    30. public void method2() {
    31. System.out.println("222:method2");
    32. }
    33.}
    34.
    35.class Implementation1Factory implements ServiceFactory{
    36.
    37. @Override
    38. public Service getService() {
    39. return new Implementation1();
    40. }
    41.
    42.}
    43.
    44.class Implementation2Factory implements ServiceFactory{
    45.
    46. @Override
    47. public Service getService() {
    48. return new Implementation2();
    49. }
    50.
    51.}
    52.
    53.public class Factories {
    54. public static void serviceCustomer(ServiceFactory fact){
    55. Service s = fact.getService();
    56. s.method1();
    57. s.method2();
    58. }
    59.
    60. public static void main(String[] args) {
    61. Factories.serviceCustomer(new Implementation1Factory());
    62. Factories.serviceCustomer(new Implementation2Factory());
    63. }
    64.}/*out
    65.111:method1
    66.111:method2
    67.222:method1
    68.222:method2
    69.*/

    乱七八糟不知道怎么归类的知识点

    接口中的域
    接口中是可以声明成员变量的,接口中的所有变量默认就是static fianl的. 所以在1.5之前,使用interface来构建枚举类型是个不错的选择.
    接口中变量的域不能是空的final类型. 但是可以通过非常量表达式赋值.

    接口的嵌套
    接口是可以相互嵌套的. 但是实在是没看懂. 以后再说吧.

  • 相关阅读:
    整合Druid数据源
    SpringBoot与数据访问
    IDEA spirng boot @Autowired注解 mapper出现红色下划线解决方法
    IntelliJ Idea 常用快捷键列表
    docker 安装mysql示例
    设计模式都没用过,好意思出去面试?
    为什么 Java 线程没有 Running 状态?
    厉害了,淘宝千万并发,14 次架构演进…
    Redis 内存满了怎么办……
    Java 线程池 8 大拒绝策略,面试必问!
  • 原文地址:https://www.cnblogs.com/thecatcher/p/5873548.html
Copyright © 2020-2023  润新知