首页 > 其他 > 详细

抽象类和接口

时间:2014-11-19 21:43:44      阅读:228      评论:0      收藏:0      [点我收藏+]
  • 简述

    接口是Java中比较重要的一个特性,为我们提供了一种将接口和实现分离的更加结构化的方法。此外,接口还可以用来实现多重继承。在了解接口之前,我们先学习一下抽象类。

  • 抽象类和抽象方法

      抽象类是指在某个类中存在抽象方法的类,不管是抽象方法一个还是多个。所谓抽象方法,就是指在类中,仅有方法声明,但没有方法体。包含抽象方法的类,必须被定义为抽象的,否则编译会报错。我们创建抽象类主要是希望创建一些通用的类,通过这个通用类去操纵一系列类。因此创建一个抽象类的对象没有意义,并且我们可能还想阻止使用者这么去做。当我们尝试去为一个抽象类创建对象时,编译器将会给出出错消息。此外,也可以创建一个不包含任何方法的抽象类。

1 //创建一个简单的抽象类  《Thinking in java》
2 abstract class Instrument
3 {
4     public abstract void play(Node n);
5     public String what(){return "Instrument";}
6     public abstract void adjust();
7 }

    接下来,以一个例子讲述抽象类

  1 package interfaces.music4;
  2 //《Thinking in Java》
  3 abstract class Instrument
  4 {
  5     private int i;
  6     public abstract void play();
  7     public String what()
  8     {
  9         return "Insrument";
 10     }
 11     public abstract void adjust();
 12 }
 13 
 14 class Wind extends Instrument
 15 {
 16     public void play()
 17     {
 18         System.out.println("Wind.play()");
 19     }
 20     public String what()
 21     {
 22         return "Wind";
 23     }
 24     public void adjust(){}
 25 }
 26 
 27 class Percussion extends Instrument
 28 {
 29     public void play()
 30     {
 31         System.out.println("Percussion.play()");
 32     }
 33     public String what()
 34     {
 35         return "Percussion";
 36     }
 37     public void adjust(){}
 38 }
 39 
 40 class Stringed extends Instrument
 41 {
 42     public void play()
 43     {
 44         System.out.println("Stringed.play()");
 45     }
 46     public String what()
 47     {
 48         return "stringed";
 49     }
 50     public void adjust(){}
 51 }
 52 
 53 class Brass extends Wind
 54 {
 55     public void play()
 56     {
 57         System.out.println("Brass.play()");
 58     }
 59     public void adjust()
 60     {
 61         System.out.println("Brass.adjust()");
 62     }
 63 }
 64 
 65 class Woodwind extends Wind
 66 {
 67     public void play()
 68     {
 69         System.out.println("Woodwind.play()");
 70     }
 71     public String what()
 72     {
 73         return "Woodwind";
 74     }
 75 }
 76 public class Music4
 77 {
 78     
 79     static void tune(Instrument i) //直接使用抽象基类,而不需要关注其类型
 80     {
 81         i.play();
 82     }
 83     static void tuneAll(Instrument []e)
 84     {
 85         for(Instrument i : e)
 86         {
 87             tune(i);
 88         }
 89     }
 90     public static void main(String []args)
 91     {
 92         Instrument []orchestra = 
 93             {
 94                 new Wind(),
 95                 new Percussion(),
 96                 new Stringed(),
 97                 new Brass(),
 98                 new Woodwind()
 99             };
100         tuneAll(orchestra);
101     }
102 }
  输出如下:
Wind.play()
Percussion.play()
Stringed.play()
Brass.play()
Woodwind.play()

    可以看到,基类作为一个抽象类,仅声明方法,而子类实现了各自所需的方法,并且在使用时(上面代码的tune和tuneAll),我们只需要使用其抽象基类,并不关注其具体类型。编译器会正确地调用相应的方法,编译器会将公共方法沿着继承层次向上移动,这样就完成了用一个通用类来操作更多的类的目标。

    下图为上述代码的继承层次结构图

bubuko.com,布布扣

     另外,如果在抽象基类的构造器中调用抽象方法,那么该抽象方法,将会被导出类的方法所覆盖。如以下列子所示:

 1 package interfaces.test;
 2 
 3 abstract class base
 4 {
 5     public base()
 6     {
 7         print();
 8     }
 9     public abstract  void print();
10     
11 }
12 
13 public class Test extends base{
14     
15     public void print()
16     {
17         System.out.println(i);
18     }
19 
20     public static void main(String []args)
21     {
22         Test a = new Test();
23         a.print();
24     }
25     private int i = 1;
26 }
27 
28 //输出  0  和 1
  • 接口

    interface关键字使得抽象的概念更进了一步,abstract类,允许人们在类中创建一个或多个非抽象方法。而interface则产生一个完全抽象的类,不提供任何一个方法的实现。接口通俗点来说,就是一组方法的集合,表示“所有实现了该特定接口的类看起来都像这样”。因此,任何使用某特定接口的代码都知道可以调用该接口的哪些方法,而且仅需要知道这些。因此,接口被用来建立类与类之间的协议,表明某个类实现了这个接口,那表示这个类一定会提供这个功能。在JAVA中,接口也被用来实现多重继承。

    首先,接口的定义要用interface关键字,要让某一个类遵循某个特定接口,需要使用implements关键字,接口也可以包含域,但这些域都是隐式的static和final。在接口中的方法,即使不被声明为public,也默认是public。因此在实现接口的方法时,必须将方法声明为public,否则编译器将会提示出错。

    

 1 package interfaces.music5;
 2 
 3 interface Instrument
 4 {
 5     int VALUE = 5;
 6     void play();
 7     void adjust();
 8 }
 9 
10 class Wind implements Instrument
11 {
12     public void play()
13     {
14         System.out.println(this+".play()");
15     }
16     public void adjust(){};
17 }
18 
19 class Percussion implements Instrument
20 {
21     public void play()
22     {
23         System.out.println(this+".play()");
24     }
25     public void adjust(){};
26     public String toString()
27     {
28         return "Percussion";
29     }
30     
31 }
32 
33 class Brass extends Percussion
34 {
35     public String toString()
36     {
37         return "Brass";
38     }
39 }
40 
41 public class Music5 
42 {
43     public static void tune(Instrument i)
44     {    
45         i.play();
46     }
47     public static void tuneAll(Instrument []e)
48     {
49         for(Instrument i : e)
50         {
51             tune(i);
52         }
53     }
54     public static void main(String []args)
55     {
56         Instrument []orch = {
57                     new Wind(),
58                     new Percussion(),
59                     new Brass()
60         };
61         tuneAll(orch);
62     }
63 }
64 //输出如下:
65 /*
66 interfaces.music5.Wind@4e81d783.play()
67 Percussion.play()
68 Brass.play()
69 
70 */

 

    可以发现以上不同,输出对象的时候,toString被程序自动调用,toString方法如果没有重载,会导致输出的不同。其次,当实现了一个接口之后,其实现就变成一个普通的类,可以按照常规方式去使用它。下图为该程序的UML图

    bubuko.com,布布扣

  • 完全解耦

    只要一个方法操作是类而非接口,那么你就只能使用这个类及其子类。接口在很大程度上放宽了这种限制,因此可以使得我们编写复用性更好的代码。下面给出一个例子。

    例如,假设有一个Processor类,其中有一个name()方法,另外还有一个process方法,process方法中接受输入参数,修改它的值,然后产生输出。以这个类作为基类扩展,用以创建不同的Processor类。

 1 package interfaces.classprocessor;
 2 
 3 import java.util.*;
 4 
 5 class Processor 
 6 {
 7     public String name()
 8     {
 9             return getClass().getSimpleName();
10     }
11     Object process(Object input)
12     {
13         return input;
14     }
15 }
16 
17 class Upcase extends Processor
18 {
19     String process(Object input)
20     {
21         return ((String)input).toUpperCase();
22     }
23 }
24 
25 class Downcase extends Processor
26 {
27     String process(Object input)
28     {
29         return ((String)input).toLowerCase();
30     }
31 }
32 
33 class Splitter extends Processor
34 {
35     String process(Object input)
36     {
37         return Arrays.toString(((String)input).split(" "));
38     }
39 }
40 
41 public class Apply 
42 {
43     public static void process(Processor p,Object s)
44     {
45         System.out.println("Using Processor "+p.name());
46         System.out.println(p.process(s));
47     }
48     
49     public static String s = "Hello World!";
50     
51     public static void main(String []args)
52     {
53         process(new Upcase(),s);
54         process(new Downcase(),s);
55         process(new Splitter(),s);
56     }
57 
58 }
59 /*输出如下
60 Using Processor Upcase
61 HELLO WORLD!
62 Using Processor Downcase
63 hello world!
64 Using Processor Splitter
65 [Hello, World!]
66 */

 

    其中Apply.process()方法可以接受任何类型的Processor,并将其应用到一个Object对象上,然后打印结果。像本例这样,创建一个能够根据所传参数对象的不同而有不同行为的方法,被称为“策略设计模式”。其中策略就是传入的参数对象。现在我们再假设发现了一组电子滤波器,它们看起来好像适用于Apply.process()方法。具体代码如下:

 1 package interfaces.filters;
 2 
 3 public class Filter 
 4 {
 5     public String name()
 6     {
 7         return getClass().getSimpleName();
 8     }
 9     public Waveform process(Waveform input)
10     {
11         return input;
12     }
13 }
14 
15 package interfaces.filters;
16 
17 public class Waveform 
18 {
19     private static long counter;
20     private final long id = counter++;
21     public String toString()
22     {
23         return "Waveform "+id;
24     }
25 }
26 
27 
28 package interfaces.filters;
29 
30 public class HighPass extends Filter
31 {
32     double cutoff;
33     public HighPass(double cutoff)
34     {
35         this.cutoff = cutoff;
36     }
37     public Waveform process(Waveform input)
38     {
39         return input;
40     }
41 }
42 
43 
44 package interfaces.filters;
45 
46 public class LowPass extends Filter
47 {
48     double cutoff;
49     public LowPass(double cutoff)
50     {
51         this.cutoff = cutoff;
52     }
53     public Waveform process(Waveform input)
54     {
55         return input;
56     }
57 }
58 
59 
60 package interfaces.filters;
61 
62 public class BandPass extends Filter
63 {
64     double lowCutoff,highCutoff;
65     public BandPass(double lowCut,double highCut)
66     {
67         lowCutoff = lowCut;
68         highCutoff = highCut;
69     }
70     public Waveform process(Waveform input)
71     {
72         return input;
73     }
74 }

 

    其中Filter跟Processor具有相同的接口元素,但是因为他们并非继承自Processor(因为Filter类并不知道你要将它作为一个Processor来用),所以你不能将Filter用于Apply.process()方法。这里主要是因为该方法和Processor之间的耦合过紧,故而导致了这种复用被禁止。但是如果Processor是一个接口,那么这种限制就会松动,使得你可以将Filter用于Apply.process()方法。下面给出修改方案:

 1 public interface Processor
 2 {
 3      String name();
 4      Object process(Object input);
 5 }      
 6 
 7 //另一个文件
 8 public class Apply
 9 {
10       public static void process(Processor p,Object s)
11       {
12              System.out.println("Using Processor "+p.name);
13              System.out.println(p.process(s));
14       }
15 }

 

    因而,复用代码的第一种方式是客户端程序员遵循该接口来编写他们的类。但是有时候,会经常碰到的情况是,你无法修改你想要使用的类。在这些情况下,就可以使用适配器模式。适配器中的代码将接受你所拥有的接口,并产生你所需要的接口。

 1 import interfaces.classprocessor.Apply;
 2 import interfaces.filters.*;
 3 import interfaces.processor.Processor;
 4 
 5 class FilterAdapter implements Processor
 6 {
 7     Filter filter;
 8     public FilterAdapter(Filter filter)
 9     {
10         this.filter = filter;
11     }
12     public String name()
13     {
14         return filter.name();
15     }
16     public Waveform process(Object input)
17     {
18         return filter.process((Waveform)input);
19     }
20 }
21 public class FilterProcessor {
22     public static void main(String []args)
23     {
24         Waveform w = new Waveform();
25         Apply.process(new FilterAdapter(new LowPass(1.0)), w);
26     }
27 //输出

      Using Processor LowPass
      Waveform 0

28 }

 

    在这种使用适配器方式中,FilterAdapter的构造器将接受你所拥有的接口Filter,然后生成你所需要的Processor接口的对象,上面的代码用到了代理。将接口从具体实现中解耦使得接口可以应用于不同的具体实现,让代码更具可用性。

 

  • Java中的多重继承

    接口不仅仅是一种更纯粹的抽象类,Java中,通过组合接口实现多重继承。C++中通过组合多个类的接口实现多重继承,但是C++中每个类都有一个具体实现,而在Java中,相同的形式,但是每个类只有一个具体实现。因此JAVA的多重继承会被C++更简便。

    如果我们要从一个非接口的类继承,那么只能从一个类去继承。其余的基元素都必须是接口。需要将所有接口名都置于implements关键字之后,用逗号将他们隔开。可以继承任意多个接口,并可以向上转型为每个接口,因为每个接口都是一个独立类型。

    使用接口的核心原因:为了能够向上转型为多个基类(以及由此带来的灵活性),另一个原因是防止客户端程序员创建该类的对象。

 

  • 通过继承来扩展接口  

    接口可以继承接口来扩展接口。

bubuko.com,布布扣

 

抽象类和接口

原文:http://www.cnblogs.com/ak47-wz/p/4107893.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!