面向对象是一种对现实世界理解和抽象的方法,当代码复杂度增加难以维护的时候,面向对象就会显得非常重要。R主要面向统计计算,而且代码量一般不会很大,几十行,几百行,使用面向过程的编程方法就可以很好地完成编程的任务。
不过,伴随着越来越多的工程背景的人的加入,R语言开始向更多的领域发展。原来的少量的代码的面向过程的编码方式,会越来越难以维护海量代码的项目,所以必须有一种新的编程方式来代码原来的面向过程的编码思路,这种新的编程方式就是面向对象编程(Object Oriented Programming, OOP)。
R语言的类有S3类和S4类,S3类用的比较广,创建简单粗糙但是灵活,而S4类比较精细,具有严格的类结构,从其他语言过来的用户可能会习惯一些。S4对象系统具有明显的结构化特征,更适合面向对象的程序设计。
由于S4对象是标准的面向对象实现方式, 有专门的类定义 setClass() 和类的实例化函数new()。 setClass函数声明如下:
setClass(Class, slots ,prototype, contains=character(), validity, sealed)
各参数含义:
Class | 定义的类名称 |
slots | 定义属性与类型, 参数为list |
prototype | 属性的默认值 |
contains | 继承的父类 |
validity | 定义属性的类型检查 |
sealed | 如果设置TRUE,则同名类不能被再次定义 |
#定义一个 人 类
setClass(
"person",
slots=list(name="character", age="numeric"),
prototype = list(name="nobody", age=20)
)
# new()实例化一个人
zhangsan <- new(Class ="person", name="zhangsan", age = 26)
# 查看 zhangsan的属性
zhangsan
An object of class "person"
Slot "name":
[1] "zhangsan"
Slot "age":
[1] 26
# zhangsan 属于person类
class(zhangsan)
[1] "person"
attr(,"package")
[1] ".GlobalEnv"
#另外,S4中我们只能使用@来访问一个对象的属性
zhangsan@name
> [1] "zhangsan"
s4类中包括强制类型检查, 如果初始化类型与定义类型不同无法通过,如
# name 传入的不是字符串
lisi <- new(Class ="person", name=11, age = 22)
Error in validObject(.Object) :
类别为“person”的对象不对: invalid object for slot "name" in class "person": got class "numeric", should be or extend class "character"
也可以使用setValidity()函数设定额外的正当性检查 , 比如年龄不可以为负数,
setValidity("person", function(object){
if (object@age <= 0) stop("Age is negative")
})
# age 传入负数
> lisi <- new(Class ="person", name="lisi", age = -1)
Error in validityMethod(object) : Age is negative
泛型函数通俗的就是同一个函数名, 遇到不同的对象产生不同的效果。
S4的泛型函数通过**setGeneric()**来定义泛型接口,通过setMethod()来定义现实类。这样可以让S4对象系统,更符合面向对象的特征。下面的例子说明了什么泛型函数
# 设定一个函数来 自我介绍
# 普通函数
selfIntroduce<- function(name){
sprintf("my name is %s", name)
}
selfIntroduce("Sun wukong")
>"my name is Sun wukong"
# 将函数定义为泛型函数,即接口
setGeneric("selfIntroduce",
function(object){
standardGeneric("selfIntroduce")
}
)
selfIntroduce("Sun wukong")# 这时普通函数已经不能使用了, 泛型函数必须指定具体类的方法
#Error in (function (classes, fdef, mtable) :
# unable to find an inherited method for function ‘selfIntroduce’ for signature #‘"character"’
# 定义函数的现实,并指定参数类型为person对象时如何运行
setMethod("selfIntroduce",
signature(object = "person"),
function(object){
sprintf("my name is %s, i'm %d years old", object@name, object@age)
}
)
selfIntroduce(zhangsan)
>"my name is zhangsan, i'm 26 years old"
# 增加一个函数的现实,指定参数类型为character对象时如何运行
setMethod("selfIntroduce",
signature(object = "character"),
function(object){
sprintf("my name is %s", object)
}
)
selfIntroduce("Sun wukong")
>"my name is Sun wukong"
# 查看类函数
> showMethods(selfIntroduce)
Function: selfIntroduce (package .GlobalEnv)
object="character"
object="Person"
# 查看类函数定义
getMethod("selfIntroduce", "person")
Method Definition:
function (object)
{
sprintf("my name is %s, i'm %d years old", object@name,
object@age)
}
Signatures:
object
target "person"
defined "person"
可以看到泛型就是遇到同的对象使用不同的方法
继承一个类使用setClass()中参数 contains指定父类
#新建 student 类, 继承 person 类。
setClass(Class = "student",
slots=list( school="character", major="character", father="person"),
contains="person")
xiaoming<- new(Class ="student",
name = "xiaoming",
age = 18,
school="qinghuauni",
major = "computer science",
father = zhangsan
)