从面向对象讲起
本来这篇文章是想作为对Java和OC中Interface/Implement/Protocol的一个补充,结果越写越多,就单独作为一个文章列出。
大学里学习计算机Tuber C的时候就讲到一个基础的概念的
- C是面向过程的语言
- C++等是面向对象的语言
我觉得90%的人都没搞明白怎么回事,但是肯定记得一句话,因为期末考试必考,面向对象语言的三个特性『封装』『继承』『多态』
封装
从Struct到Class
C中只有结构体,结构体只能组装基本的数据类型(int char之类的),不能够加函数的,调用C的函数只能书写函数名,也就是说逻辑处理功能是独立于数据的。这样使用者在完成逻辑功能时就需要满足以下条件
- 我要使用哪些数据
- 然后我要使用哪些函数
这样明显显得比较笨,代码一旦多起来,很多人不知道哪些数据可以用于哪些函数,而且一个一个去找这些函数也很麻烦。
所以人们就诞生了面向对象编程的概念Class,数据和方法放在一起,称为Object(对象),这样使用者只需要记住
- 我要使用哪个Object
数据和逻辑处理全部放在Object里,查找起来也很方便
PS:
结构体Struct如果想调用函数,需要把C语言函数的指针存在一个变量放入结构体,其实Class也是基于这个原理。
以上把函数放入结构体,并起了个新名字的过程就是封装的过程,并且产生了「Class」「Object」的概念
Java和OC的封装
Java是基于C++而OC是C的超集,同样是面向对象的语言,自然就会有自己的Class和Object
//Java中所有的Class都默认继承了这个类
public class Object {}
//OC就比较喜欢敞开大屌说亮话,你声明的所有类都会继承于这个类
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
@end
例如
//Java虽然没有extends但是也继承于Object
public class BaseObject {}
//OC肯定会继承于NSObject
@interface BaseObject : NSObject
@end
继承
面向对象的语言都具有继承特性,可以获得另外一个相同定义的所有公开的成员变量和方法,注意这里用到的是『相同定义』
//Java中的接口只能继承接口
public interface Collection<E> extends Iterable<E> {}
public interface List<E> extends Collection<E> {}
//Java中的类继承只能继承类
public abstract class AbstractList<E> extends AbstractCollection<E>
public class ArrayList<E> extends AbstractList<E>
//OC中只有类有继承
@interface BaseObject : NSObject
@end
这里需要强调下Java和OC都是单继承,不能同时继承多个类,如果要继承2个类就需要用3个Class分两层继承。
多态
多台是指同一个函数,可以有多个可能的实现,但是函数名一样,这里就不细讲了,去看教科书的画圆形画正方形画长方形的举例吧。
多态为了解决什么问题
呃。。还是简单说下吧。。。比如有个函数叫countSum(),有两个类都有这个方法
A.countSum() //输出了Asum
B.countSum() //输出了Bsum
正常情况下是怎么发生的,当然会说因为我在AB中都写了countSum函数并写了不同的逻辑,但是如果你写上100个Class,你能保证每次你起的函数名都不拼错么
就是为了解决这个问题,所以提出了多态机制是使用同个函数名,但是可以产生不同输出的机制
多态的机制
后来高级码农发现了两个问题
- 我只想起好函数名,大家都用我这个名字,具体干嘛让小弟去写好了
- 妈蛋虽然我比较NB,但是我感觉这个函数还是有问题,干脆暂时先用着谁看着不爽再自己重新写一遍好了,但是不能让他改我现在的。
这里就提出了多态的基本概念「仅有函数定义的多态」「通过重载获得的多态」
仅有函数定义的多态(问题1)
就是只起好函数名,具体实现没写,既然没有实际代码,那也就没有实例化的需求了,所以这样的概念不管在哪个语言里,仅仅是Class定义,不可能有Object
C++ | Java | Object-C | |
---|---|---|---|
实现 | pure virtual | interface | protocol |
特点 | 仅能在在基类中定义 | interface都不能new | protocol不能alloc |
通过这种手段确定好函数名,而且解决了问题1
通过继承和重载获得的多态(问题2)
根据面向对象的基本要求『继承』会使得两个Object获得相同的的函数,那么在子函数里再次书写该函数的时候,就可以得到不同的逻辑。
通过抽象类获得多态(问题2)
后来人们发现,我需要实现一些简单的逻辑,但是又只想让你继承我的来使用,不要实例化我这个类本身,因为逻辑不全,你看着不爽的地方可以继承之后改掉。这个就是「抽象类」的概念。
C++ | Java | Object-C | |
---|---|---|---|
实现 | abstract class | abstract class | 无 |
特点 | 不能实例化且只能是基类 | 不能被实例化,可以继承其它abstract | 无 |
但是在Objective-C中不存在抽象类这个概念。
多态的总结
- 任何语言都可以通过继承后重载方法进行多态的实现
除此之外
- C++的多多态就是『抽象类』和『虚函数』『纯虚函数』
- 因为Java是基于C++的所以其对多态有进行了整理,包括『抽象类』『接口类』
- Object-C表示尼玛搞的这么复杂,只支持了『Protocol』协议
其中不能实例化的是
C++ | Java | Object-C |
---|---|---|
抽象类/包含纯虚函数的类 | 抽象类/接口类 | Protocol |
其中C++是最为灵活的,只要声明了纯虚函数或者抽象类就可以防止别人进行实例化
Java把C++中「仅包含纯虚函数不包含成员变量的类」称之为「接口类」,并且和C++可以多继承不同,接口只可以实现,Java用单继承多实现的方式完成了多继承。
而Objective-C因为和C++没有半毛钱关系,所以只采取了Protocol一种方法实现多态,而且和Java一样通过单继承多协议来完成多继承的功能
!!!但是这里要注意!!!一个关键的问题来了
由于Java基于C++,Java的Interface本质还是类,而Objective-C不同,Protocol本身和类没有关系!!!!所以说 『Java的Interface ≠ OC的Protocol』
OC无函数重写的多态
都讲到了这里,还有一个点没有讲到,就是Java和C++都拥有的一个特性,函数重写
void method(int a);
int method(int a);
void method(char a);
在C++和Java中如果使用同一个函数名且入参个数相同的时候,在「返回值」「参数类型」有一个不同的时候,都会被编译器判定为不同的函数,也算是一种「变相的多态」,因为C++在编译时,函数是会读返回值和参数的,你只关心了函数名,觉得是一种多态,严格来讲,编译器认为它们本来就是不同的函数,不存在多态。
但是OC呢
- (void)method:(NSString *) parameter key:(NSString *)key;
- (void)method:(NSString *) parameter2 key:(UIView *)view;
对于OC的编译器来说,它只读取函数的命名,并且不读取参数的类型,所以说以上两个函数在编译器看来都是
method:key:
这个在创建selector的时候经常会碰到,所以说OC不能通过改变返回值和入参类型进行重写达到「变相的多态」