学习笔记,仅供参考,有错必纠
参考自:《R的极客理想》-- 张丹
S4对象系统是一种R语言面向对象实现方式,S4对象有明确的类定义、参数定义、参数检查、继承关系、实例化、接口函数、实现函数等面向对象系统的特征。
S4对象系统有专门的类定义函数setClass和类的实例化函数new,我们先看看setClass的语法。
setClass语法:
setClass(Class, representation, prototype, contains=character(),
validity, access, where, version, sealed, package,
S3methods = FALSE, slots)
setClass参数表:
参数 | 含义 |
---|---|
Class | 定义类名 |
slots | 定义属性和属性类型 |
prototype | 定义属性的默认值 |
contains=character() | 定义父类,继承关系 |
validity | 定义属性的类型检查 |
where | 定义存储空间 |
sealed | 如果设置TRUE,则同名类不能被再次定义 |
package | 定义所属的包 |
通过setClass函数定义类的结构,再通过new函数来实例化类对象:
#定义一个S4对象
setClass("Person", slots = list(name = "character", age = "numeric"))
#实例化一个Person对象
father <- new("Person", name = "F", age = 50)
father
#输出start
An object of class "Person"
Slot "name":
[1] "F"
Slot "age":
[1] 50
#输出end
class(father)
#输出start
[1] "Person"
attr(,"package")
[1] ".GlobalEnv"
#输出end
otype(father) #"S4"
如果需要创建有继承关系的S4对象,可以通过setClass函数的contains属性来设置父类:
#定义一个S4对象Person
setClass("Person", slots = list(name = "character", age = "numeric"))
#定义一个S4对象Son, 继承Person
setClass("Son", slots = list(father = "Person", mother = "Person"),
contains = "Person")
#实例化一个Person对象
father <- new("Person", name = "F", age = 50)
mother <- new("Person", name = "M", age = 51)
#实例化一个Son对象
son <- new("Son", name = "S", age = 22,
father = father, mother = mother)
son@name #"S"
son@age #22
son@father
#输出start
An object of class "Person"
Slot "name":
[1] "F"
Slot "age":
[1] 50
#输出end
#查看son对象的mother属性
slot(son, "mother")
#输出start
An object of class "Person"
Slot "name":
[1] "M"
Slot "age":
[1] 51
#输出end
# 检查son类型
otype(son) #"S4"
# 用isS4()检查S4对象的类型
isS4(son) #TRUE
#检查son@mother属性类型
otype(son@mother) #"S4"
通过setClass函数的prototype属性给属性字段中定义的参数设置默认值:
#定义一个S4对象Bunny
setClass("Bunny", slots = list(name = "character", age = "numeric"),
prototype = list(age = 20))
b <- new("Bunny", name = "Huang")
b
#输出start
An object of class "Bunny"
Slot "name":
[1] "Huang"
Slot "age":
[1] 20
#输出end
通过setValidity函数给属性字段中定义的参数设置类型检查:
#定义一个S4对象Person
setClass("Person", slots = list(name = "character", age = "numeric"))
setValidity("Person", function(object) {
if (object@age <= 0 | object@age >= 100) {
stop("年龄非法")
}
})
p <- new("Person", name = "T", age = -1)
#报错start
Error in validityMethod(object) : 年龄非法
#报错end
S4对象还支持从一个已实例化的对象中创建新对象,创建时可以覆盖对象的值:
# 创建一个对象实例n1
n1 <- new("Person", name="n1", age=19)
# 从实例n1中创建实例n2,并修改name的属性值
n2<-initialize(n1, name="n2")
n1
#输出start
An object of class "Person"
Slot "name":
[1] "n1"
Slot "age":
[1] 19
#输出end
n2
#输出start
An object of class "Person"
Slot "name":
[1] "n2"
Slot "age":
[1] 19
#输出end
在S3对象中,一般我使用$
来访问一个对象的属性。但在S4对象中,我们只能使用@
来访问一个对象的属性。
#定义一个S4对象
setClass("Person", slots = list(name = "character", age = "numeric"))
#实例化一个Person对象
father <- new("Person", name = "F", age = 50)
#访问S4对象的属性
father@name #"F"
slot(father, "name") #"F"
S4的泛型函数实现有别于S3的实现,S4分离了方法的定义和实现,如在其他语言中我们常说的接口和实现分离。通过setGeneric来定义接口,通过setMethod来定义实现函数。
普通函数的定义和调用:
work <- function(x) cat(x, "is working")
work("Huang") #Huang is working
S4的泛型函数:
#定义一个Person类
setClass("Person", slots = list(name = "character", age = "numeric"))
#定义泛型函数work,即接口
setGeneric("work", function(object) standardGeneric("work"))
#定义work的实现函数,并指定参数类型为Person类的对象
setMethod("work", signature(object = "Person"),
function(object) cat(object@name, "is working"))
p <- new("Person", name = "p1", age = 18)
work(p) #p1 is working
通过S4对象系统,把原来的函数定义及调用过程从2步变成4步:
当我们使用S4对象进行面向对象封装后,我们还需要能查看S4对象的定义和函数定义.
R>#检查work的类型
R>ftype(work)
[1] "s4" "generic"
R>#直接查看work函数
R>work
standardGeneric for "work" defined from package ".GlobalEnv"
function (object)
standardGeneric("work")
Methods may be defined for arguments: object
Use showMethods("work") for currently available ones.
R>#查看work函数的现实定义
R>showMethods(work)
Function: work (package .GlobalEnv)
object="Person"
R>#查看Person对象的work函数现实
R>getMethod("work", "Person")
Method Definition:
function (object)
cat(object@name, "is working")
Signatures:
object
target "Person"
defined "Person"
R>#检查Person对象有没有work函数
R>existsMethod("work", "Person")
[1] TRUE