抽象类

来源:互联网 发布:jersey json jackson 编辑:程序博客网 时间:2024/03/29 22:22

抽象类

Abstract Classes

Control的每个子类都应自己实现DrawWindow()方法,但没有硬性的要求。要指定子类必须实现基类的方法,应该指明该方法是抽象的(abstract)。

抽象方法没有实现。它只创建派生类都要实现的方法名和签名。而且,使类的一个或多个方法为抽象的,也会有一个副效应:会使类也变成抽象的。

抽象类是派生类的基,但是实例化抽象类的对象却是非法的。一旦声明方法为抽象的,就不能创建该类的任何实例。

这样,如果将Control类中的DrawWindow()指定为abstract,就可以从Control派生,但不能创建任何Control对象。每个派生类都要实现DrawWindow()。如果派生类没有实现抽象方法,类就也是抽象的,也不能有实例。

将方法指定为abstract,是通过在方法定义前加上abstract 关键字实现的,如下所示:

abstract public void DrawWindow();

(因为方法没有实现,也就没有大括号,只有一个分号。)

如果一个或多个方法为抽象的,类定义也要标为abstract的,如下所示:

abstract public class Control

示例5-2演示了抽象类Control和抽象方法DrawWindow()的创建。

示例5-2:使用抽象类和方法

#region Using directives

using System;

using System.Collections.Generic;

using System.Text;

#endregion

namespace abstractmethods

{

   using System;

   abstract public class Control

   {

      protected int top;

      protected int left;

      // 构造方法有两个整数参数

      // 以确定控制台上的位置

      public Control( int top, int left )

      {

         this.top = top;

         this.left = left;

      }

      // 模拟窗口绘制

      // 注意:没有实现

      abstract public void DrawWindow();

   }

// ListBox 派生自 Control

   public class ListBox : Control

   {

      private string listBoxContents;  // 新的成员变量

      // 构造方法添加一个参数

      public ListBox(

         int top,

         int left,

         string contents ):

      base(top, left)  // 调用基构造方法

      {

         listBoxContents = contents;

      }

示例5-2:使用抽象类和方法(续例)

      // 重定义版本实现

      // 抽象方法

      public override void DrawWindow()

      {

         Console.WriteLine( "Writing string to the listbox: {0}",

            listBoxContents );

      }

   }

   public class Button : Control

   {

      public Button(

         int top,

         int left ):

      base(top, left)

      {

      }

      // 实现抽象方法

      public override void DrawWindow()

      {

         Console.WriteLine( "Drawing a button at {0}, {1}/n",

            top, left );

      }

   }

   public class Tester

   {

      static void Main()

      {

         Control[] winArray = new Control[3];

         winArray[0] = new ListBox( 1, 2, "First List Box" );

         winArray[1] = new ListBox( 3, 4, "Second List Box" );

         winArray[2] = new Button( 5, 6 );

         for ( int i = 0; i < 3; i++ )

         {

            winArray[i].DrawWindow();

         }

      }

   }

}

示例5-2中,Control类被声明为抽象的,因此不能实例化。如果将第一个数组成员:

winArray[0] = new ListBox(1,2,"First List Box");

替换为以下代码:

winArray[0] = new Control(1,2);

则程序会出现如下错误:

无法创建抽象类或接口'Control'的实例

我们可以实例化ListBox和Button对象,因为这些类重定义了抽象方法,使类变成具体(concrete)而不是抽象的了。

抽象的局限

Limitations of Abstract

虽然将DrawWindow()指定为抽象的,会强制要求所有派生类实现此方法,但这是局限性很大的解决方案。如果从ListBox派生一个类(如DropDownListBox),就无法强制派生类必须实现DrawWindow()方法。

提示:C++程序员注意,C#中为Control.DrawWindow()提供实现是不可能的,因此不能利用公用的被派生类共享的DrawWindow()子例程。

最后,抽象类不仅是一种实现技巧,它还代表了一种抽象的理念,要为所有派生类创建一个“合同(contract)”。也就是说,抽象类描述了要实现该抽象的所有类的公共方法。

抽象类Control应该反映所有Control的共性,即使永远不用实例化抽象的Control本身。

抽象类的概念,顾名思义,应该实现各种Control的具体实例都具有的“Control”这个抽象的概念。比如浏览器窗口、图文框、按钮、列表框、下拉列表框,等等。抽象类确立了什么是Control这一概念,虽然,我们并没有真正创建一个“Control”。使用abstract的一种替换方式是定义一个接口,将在第八章讲述。

密封类

Sealed Class

与抽象相对的设计概念是密封(sealed)。抽象类是用来被派生,并为其子类提供遵循模板;而密封类则完全不允许被派生。关键字 sealed置于类声明之前将阻止进行派生。类被标记为sealed往往就是为了防止偶然继承。

提示:Java程序员注意了,C#中的密封类等价于Java中的final类。

如果示例5-2中的Control声明从abstract改为sealed(同样也要从DrawWindow()声明中删去abstract 关键字),程序就无法编译了。如果进行编连,编译器会返回如下错误信息:

'ListBox'无法从密封类'Control'继承

还有其他信息(例如不能在密封类中创建新的保护成员)。