我对抽象类感到困惑,我知道这不是一个与编程问题有关的问题。但这是我能找到解决方案的唯一地方。
众所周知,抽象类无法实例化,您无法在这些类中实现任何内容。我们可以派生这些类,并可以在派生类中实现。所以我的问题是:
如果我们必须在派生类中实现逻辑,我们必须在派生类中使用完整的函数和类,那么我们为什么要创建抽象类?
有人可以给我一个明确的解释吗?如果有任何好的例子,那么请告诉我
答案 0 :(得分:4)
当您使用抽象类时,您在抽象类中有一些实现,但是将特定于派生类的位保留为抽象类。例如,我可以为所有形状定义一个抽象类:
abstract class Shape
{
public int Sides{get;protected set;}
public abstract int CalculateArea();
}
所有形状都有特定数量的边,因此可以进入一般情况(即Shape
)。区域的计算取决于形状的类型(三角形,正方形等),因此如果属于派生类。但是,我希望我的类层次结构的用户能够从任何CalculateShape
实例调用Shape
。
我如何创建派生类并覆盖CalculateLength
:
class Square : Shape
{
public Square(int sideLength)
{
this.Sides = 4;
this.SideLength = sideLength;
}
public int SideLength{get;private set;}
public override int CalculateArea()
{
return this.SideLength * this.SideLength
}
}
请注意,我没有重新实现Shape
中的所有Square
,我只实现了Square
个特定位。
答案 1 :(得分:3)
根据MSDN,
无法实例化抽象类。抽象类的目的是提供多个派生类可以共享的基类的公共定义。例如,类库可以定义一个抽象类,用作其许多函数的参数,并要求程序员使用该库通过创建派生类来提供自己的类实现。
以上是所有技术解释,在外行风格中,它就像创建一个蓝图,提供信息它可以做的所有事情,所有派生类将提供自己的过程如何做到这一点。
例如,我们可以考虑Driver
,我们知道有许多驱动程序类型,例如auto-rickshaw driver
,truck driver
,tractor driver
等,但它们都有一个共同的分母功能{ {1}} Driving
每个进程都会有所不同,因此
Driving
:具有Driver
功能的抽象类
Driving
:派生类扩展Auto-RickshawDriver
类
你的问题,
如果我们必须在派生类中实现逻辑,并且我们必须在派生类中使用完整的函数和类,那么我们为什么要创建抽象类?
仅仅因为我们想要定义一些共同的功能,每个其他类都必须遵守它的成员。一般而言,基于功能创建组,即我们可以说该类属于一个组,我们确信该组的那些常见功能将存在。
答案 2 :(得分:1)
抽象类将包含常见的不完整实现,然后可以从多个派生类中重用它们。
在.NET框架中,您可以找到许多此类抽象类的示例,例如System.IO.Stream
使用所有这些类的代码可以使用Stream
的公共公共接口来引用它们,而不必知道它使用的具体Stream
实现。
答案 3 :(得分:1)
只是为了纠正一个小细节,抽象类可以是部分抽象的或完全抽象的,这取决于它们是否部分实现(在这种情况下我们通常称它们为抽象类)或根本没有实现并且没有成员变量(在这种情况下,我们通常称它们为 interfaces )。
它们是抽象类的许多用途,其中大多数意味着能够在不修改此代码的情况下扩展代码行为。
假设您想编写一个从键盘读取数据并将其写入屏幕的程序。 (我将使用C#代码,因为它比C ++略短)
void Copy() {
int c;
while((c = Console.Read()) != -1) {
Console.Write(c);
}
}
现在,要求发生了变化。您希望能够从文件而不是键盘读取,具体取决于变量。
TextReader reader;
void Copy(bool readFromFile) {
int c;
while((c = ReadNextChar(readFromFile)) != -1) {
Console.Write(c);
}
}
int ReadNextChar(bool readFromFile) {
if(readFromFile) {
return reader.Read();
} else {
return Console.Read();
}
}
现在,您希望能够从网络中读取数据。 bool
变为enum
,if
/ else
变为switch
/ case
等。
enum ReadMode { FROM_CONSOLE, FROM_FILE, FROM_NETWORK };
TextReader fileReader;
TextReader networkReader;
void Copy(ReadMode readMode) {
int c;
while((c = ReadNextChar(readMode)) != -1) {
Console.Write(c);
}
}
int ReadNextChar(ReadMode readMode) {
switch(readMode) {
case FROM_CONSOLE:
return Console.Read();
case FROM_FILE:
return reader.Read();
case FROM_NETWORK:
return networkReader.Read();
default:
return -1;
}
}
这可以继续下去。写作可能会出现同样的要求。该程序的问题在于它明确列出了您可以从文件中读取的所有方法。要从其他来源阅读,您必须修改代码(将成员添加到enum ReadMode
并在case
中添加ReadNextChar
。如果您有权访问来源,您可能会认为这是正常的代码,但如果它是一个外部库,你可能会认为这种做法很糟糕,因为你无法扩展这个精彩的拷贝库。
现在遇到抽象类。如果您将所有资源抽象为单个Source
概念,那该怎么办呢?因为毕竟,您只需要向来源询问int
。所以让我们写下这个抽象的概念。
interface Source {
int ReadNextChar();
}
所有三个实现:
class KeyboardSource : Source {
int ReadNextChar() {
return Console.Read();
}
}
class FileSource : Source {
TextReader reader;
FileSource(string path) {
reader = new StreamReader(path);
}
int ReadNextChar() {
return reader.Read();
}
}
class NetworkSource : Source {
TextReader reader;
NetworkSource (string url) {
reader = new StreamReader(path);
}
int ReadNextChar() {
return reader.Read();
}
}
并实现复制功能:
void Copy(Source source) {
int c;
while((c = source.ReadNextChar()) != -1) {
Console.Write(c);
}
}
现在要添加任何源类型,只需实现Source
的新衍生,而无需修改现有代码。 Copy
计划保持正确且不受影响,Source
的现有衍生工具也是如此。
这称为多态。如果查看Copy
的依赖关系,它们不包含有关文件,网络或用户界面的代码。它可以打包在一个库中,允许用户扩展它可以读取的源类型。
希望它很清楚。写作部分也可以这样做。
答案 4 :(得分:0)
注意这个例子:
abstract class A
{
// forces subclasses to implement
abstract void method1();
virtual void method2()
{
// some common logic
// that sublcasses can
// modify
}
protected void method3()
{
// some common logic
}
}
class B : A
{
void method1()
{
// needs to be implemented
}
override void method2()
{
// optional: e.g. something added
base.method1();
}
}
class C : A {
//...
}
不,您可以在基类中指定通用逻辑,以及定义每个子类将具有的抽象方法。所以你的基类就像一个界面。您可以将method1()
调用到B和C的每个实例。
所以:不!这绝对是不浪费时间; - )。
答案 5 :(得分:0)
不是,
假设你有一个基类
class Foo{
public String getGreetingMessage(){
return "Hello " + getName();
}
public abstract String getName();
}
你有一个派生类
class Bar extends Foo{
public String getName(){
return "MeMamo";
}
}
class Boo extends Foo{
public String getName(){
return "Boo";
}
}
在另一个类中,我从derived,
调用方法Bar b = new Bar();
System.out.println(b.getGreetingMessage()); // Hello MeMamo.
Boo boo = new Boo();
System.out.println(boo.getGreetingMessage()); // Hello Boo;
我不需要再次编写基本方法。 假设另一个派生类的操作。