抽象类

什么是抽象类?

  • 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
  • 抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
  • 由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
  • 父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。

——来自菜鸟教程

Java和C++的抽象类

Java抽象类

  • 在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口.
  • Java中的抽象类是用 abstract class关键词来实现的.
    • 例如我们在简单了解三种工厂模式中的 AbstractFactory抽象工厂
    • 此工厂不能被直接实例化
      • 实例化的话,必须实现抽象方法
      • 或者子类继承抽象类,子类中实现抽象方法.再使用子类

C++抽象类

  • 如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。
  • 纯虚函数是通过在声明中使用 “= 0” 来指定的
  • 此类不可以被实例化
  • 抽象类也可以包含非纯虚函数,成员变量
  • 如果父类是抽象类,子类没有完全重写纯虚函数, 那么子类也是抽象类
1
2
3
4
5
6
7
8
9
class Box{
   public:
      // 纯虚函数
      virtual double getVolume() = 0;
   private:
      double length;      // 长度
      double breadth;     // 宽度
      double height;      // 高度
};

ObjC模拟抽象类

在ObjC中是没有抽象类的概念的, 但是我们可以通过协议Protocol来间接实现抽象类.

  • 创建一个抽象类 AbstractAnimal

  • 创建一个协议 AnimalProtocol 协议中有需要实现的方法 run(), eat().

    • AbstractAnimal 遵守 AnimalProtocol协议
    • 而由于每个动物跑步 和 吃饭是不一样的
    • 所以让 AbstractAnimal 实现协议方法,明显不合适
  • 所以我们再创建继承 AbstractAnimal 的子类,比如Dog

    • Dog中实现具体的 Dog 自己的 run() , eat() 方法
  • 因为 AbstractAnimal 不能实例化,所以在初始化方法中,如果判断初始化的类不是抽象类的子类,直接抛出异常

    1
    
        NSAssert(![self isMemberOfClass:[AbstractAnimal class]], @"AbstractDownloader is an abstract class, you should not instantiate it directly.");
    
  • 在调用 AbstractAnimal 中的 run(), eat() 方法时,直接抛出必须覆写方法的异常

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    //  在.m文件中最好做如下处理
    #define MethodNotImplemented() \
        @throw \
        [NSException exceptionWithName:NSInternalInconsistencyException \
                                reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] \
                              userInfo:nil]
      
    - (void)run{
        MethodNotImplemented();
    }
      
    - (void)eat{
        MethodNotImplemented();
    }
    

代码如下 :

AnimalProtocol协议:

1
2
3
4
5
6
@protocol AnimalProtocol <NSObject>

- (void)run;
- (void)eat;

@end

AbstractAnimal实现文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@interface AbstractAnimal : NSObject<AnimalProtocol>

@end

#import "AbstractAnimal.h"

//  在.m文件中最好做如下处理
#define MethodNotImplemented() \
    @throw \
    [NSException exceptionWithName:NSInternalInconsistencyException \
                            reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] \
                          userInfo:nil]

@implementation AbstractAnimal

- (instancetype)init{
    NSAssert(![self isMemberOfClass:[AbstractAnimal class]], @"AbstractDownloader is an abstract class, you should not instantiate it directly.");
    return [super init];
}

- (void)run{
    MethodNotImplemented();
}

- (void)eat{
    MethodNotImplemented();
}

@end

Dog类 继承于

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@interface Dog : AbstractAnimal

@end

@implementation Dog

- (void)run{
    NSLog(@"%s",__func__);
}

- (void)eat{
    NSLog(@"%s",__func__);
}

@end

总结:

  • 以上,我们就完成了 ObjC模拟抽象类 的实现.
  • 当有多种动物,比如Cat
    • 我们需要创建Cat,使 Cat继承于 AbstractAnimal
    • 并实现 run() 和 eat() 方法
    • 完成以上两个步骤,我们就完成了, 新类的定制

ObjC中模拟抽象类的使用

Masonry

Masonry中的MASConstraint就是一个抽象类。

拿出一段代码举例 :

1
2
#pragma mark - Abstract
- (MASConstraint * (^)(CGFloat multiplier))multipliedBy { MASMethodNotImplemented(); }

multipliedBy方法的具体实现却在

MASViewConstraint 类中, 查看 继承关系

1
2
3
4
5
6
7
8
9
- (MASConstraint * (^)(CGFloat))multipliedBy {
    return ^id(CGFloat multiplier) {
        NSAssert(!self.hasBeenInstalled,
                 @"Cannot modify constraint multiplier after it has been installed");
        
        self.layoutMultiplier = multiplier;
        return self;
    };
}

查看 MASViewConstraint 继承关系, 果然是继承于 MASConstraint

1
2
3
4
5
/**
 *  A single constraint.
 *  Contains the attributes neccessary for creating a NSLayoutConstraint and adding it to the appropriate view
 */
@interface MASViewConstraint : MASConstraint <NSCopying>

NSCoder

  • NSCoder 是一个抽象类
  • 不能被直接使用,只能实例化子类并使用子类。
  • 以下内容来自苹果官方文档

NSCoder

An abstract class that serves as the basis for objects that enable archiving and distribution of other objects.

Language

SDKs

  • iOS 2.0+
  • macOS 10.0+
  • tvOS 9.0+
  • watchOS 2.0+
  • Mac Catalyst 13.0+

Framework

  • Foundation

On This Page


Declaration

1
@interface NSCoder : NSObject

Overview

NSCoder declares the interface used by concrete subclasses to transfer objects and other values between memory and some other format. This capability provides the basis for archiving (storing objects and data on disk) and distribution (copying objects and data items between different processes or threads). The concrete subclasses provided by Foundation for these purposes are NSArchiver, NSUnarchiver, NSKeyedArchiver, NSKeyedUnarchiver, and NSPortCoder. Concrete subclasses of NSCoder are “coder classes”, and instances of these classes are “coder objects” (or simply “coders”). A coder that can only encode values is an “encoder”, and one that can only decode values is a “decoder”.

NSCoder operates on objects, scalars, C arrays, structures, strings, and on pointers to these types. It doesn’t handle types whose implementation varies across platforms, such as union, void *, function pointers, and long chains of pointers. A coder stores object type information along with the data, so an object decoded from a stream of bytes is normally of the same class as the object that was originally encoded into the stream. An object can change its class when encoded, however; this is described in Archives and Serializations Programming Guide.

The AVFoundation framework adds methods to the NSCoder class to make it easier to create archives including Core Media time structures, and extract Core Media time structure from archives.

—–引用自苹果官方文档

参考文献:

用Objective-C实现抽象类

Objective-C中的抽象类

Java 抽象类