S4类
1.基于对象的编程
1.1 定义
## 定义S4类,以下两者等价
setClass("BMI", representation(weight="numeric", size="numeric"))
## representation函数定义slot
setClass("BMI", slots=c(weight="numeric", size="numeric")) #
## 定义方法
setMethod(f, signature, definition) # f为泛型函数名,signature指定对象类名,definition定义方法
## 定义S4对象
new( "BMI", weight=1,size=1 )
1.2 检查
S4类特点:
- 类型检查
- 有效性检查设置
setValidity("BMI", function(object) {
if (object@size < 0) {
return("negative Size")
} else {
return(TRUE)
}
})
## 若size属性<0会报错,报错信息为:negative Size
1.3 继承
setClass("BMIplus",representation(sex="character"),contains="BMI")
## contains定义继承,继承父类的属性
此外,可封装
S4对象包括:类(class)、变量(slot)、方法(methods)
slots定义类后,不能变更
methods操作:创建、校验(定义规则)、属性操作、其他
2. 面向对象编程基础
2.1 类的声明
Object@slot <-
获取属性并赋值,应避免此类赋值,此外attr、attributes可用可任意赋值跳过检查,应避免
setClass(Class= , slots= , prototype= )
prototype定义默认值,可不定义,系统默认空
removeClass()
移除类,但相关方法并不移除
查看对象的函数方法:
-
slotNames()
查看slots的名 -
getSlots()
查看slots的名与类 -
getClass()
查看slots的名与类以及相关的继承
2.2 方法
2.2.1 setmethod()
定义已存在的泛型函数
setMethod( f="plot", #f为已定义好的泛型函数名
signature="BMI", #S4类名
definition=function(x,y...){...} #参数应保持与泛型函数一致
)
show()
方法为展示对象时自动运行的函数,例如new("BMI")
或A
自动引发此函数
2.2.2 setGeneric()
定义新的S4泛型函数
setGeneric (
name= "countMissing",
def=function(object){standardGeneric("countMissing")} #指派S4泛型函数
)
## 可用setMethod定义对应类的方法
lockBinding("countMissing",.GlobalEnv) #锁定方法,禁止修改
2.2.3 查看方法
showMethods(class= )
查看类的方法
getMethod(f="plot",signature="Trajectories")
查看对应类的方法代码
existsMethod(f="plot",signature="Trajectories")
返回是否存在此函数
2.3 构造
2.3.1 校验
setClass(Class="Trajectories",
representation(times="numeric",traj="matrix"),
validity=function(object){ ##设置校验标准
if(length(object@times)!=ncol(object@traj)){
stop ("[Trajectories: validation] the number of temporal measurements does not correspond") ##设置报错信息,也可用return
}else{
return(TRUE)
}})
new("Trajectories") #空的对象并不触发校验器
## setValidity也有此作用
2.3.2 初始化
## initialize方法在new()定义类的对象时初始化对象,
setMethod(f = "initialize", signature = "Trajectories",
definition = function(.Object,times,traj){
#.Object为类,后跟new()中应出现的参数,不一定为slots,可在此函数中定义规则计算slots使符合类的slots
cat("~~~~~ Trajectories: initializator ~~~~~ \n")
if (!missing(traj)) {
colnames(traj) <- paste("T", times, sep = "")
rownames(traj) <- paste("I", 1:nrow(traj), sep = "")
.Object@times <- times
.Object@traj <- traj
validObject(.Object) # 触发校验器
return(.Object)
}
})
2.3.3 自建构造器
trajectories <- function(times, traj) {
if (missing(times)) { #未定义时自行定义
times <- 1:ncol(traj)
}
new(Class = "Trajectories", times = times, traj = traj)
}
## 避免重复输入Class="Trajectories"参数
## 亦可在自定义函数中自定义slots的内容计算规则,自动调用校验器
由于初始化器定义了输入规则,构造新对象规则时难修改,且需定义触发校验器,推荐使用自建构造器取代初始化器
2.4 存储器
## 获取slot
## @也可,但由于可以随意赋值,危险!!!
setGeneric("getTraj",function(object){standardGeneric("getTraj")}) #设置获取泛型函数
setMethod("getTraj", "Trajectories", function(object) {
return(object@traj)
})
## 修改slot
setGeneric("setTimes<-",function(object,value){standardGeneric("setTimes<-")})
setMethod("setTimes<-","Trajectories", ##也可用setReplaceMethod,此时不用加<-
function(object,value){
object@times <-value
validObject(object) ## 可触发校验器
return(object)
})
a=new("Trajectories")
setTimes(a)<-1:4
## 若设置slot,需定义set<-函数族,这样才可覆盖原对象
## 若定义函数setTimes,则value参数应在()内,并不改变原对象
## "[" "[<-"虽然在S4不可用,但在定义后可用
setMethod(
f= "[",
signature="Trajectories",
definition=function(x,i,j,drop){
if(i=="times"){return(x@times)}else {}
if(i=="traj"){return(x@traj)}else {}
}
)
3. 面向对象编程进阶
3.1 方法参数
- signature
signature=c(x="numeric",y="character")
定义包含多类的参数方法,注意类的个数应与参数个数对应
若signature中的参数类未定义, 则默认为“ANY”类,任一均试用。
此外还可定义signature中的参数类为"missing",此时当此参数缺省时试用
3.2 继承
继承于多个类是危险的。
setClass("son",slots=c(),contain="father"
定义继承,父继承的slots可不写;
unclass()
可以展示所有slots包括继承的
hasMethod("initialize","TrajPartitioned")
查找方法与existsMethod不同的是,会查找父类方法
selectMethod("initialize","TrajPartitioned")
显示代码方法,与getMethod不同的是会查找父类方法
setMethod(
f="print",
signature="TrajPartitioned",
definition=function(x,...){
callNextMethod() ##触发父类继承方法
cat("the object also contains",length(x@listPartitions),"partition")
return(invisible())
}
)
callNextMetod
比较局限,因为其完全依赖于父类对象
as(object, Class)
将object转化为继承的Class,此转化后的对象可用转化后对象的方法
is(object,class2)
判断object是否属于class2,包含继承关系
as(objectSon,"ClassFather") <- objectFather
将子类转化为父类并且对应的slots赋值为父类对象的slots
setIs(class1="son", class2="father", ##setIs定义超类,可将不继承的类定义继承
test=function(from){if()return(TRUE)}, ##定义条件超类,此情况并不继承超类方法
coerce=function(from,to){to=new(to)
return(to)}, ## coerce定义转化方法,用于as
replace=function(from,value){return(from)}) ## replace定义替换,用于as<- value
## from对应class1,to对应class2
setAs(from, to,
def=function(from,to){to=new(to);return(to)}, ##定义as
replace=function(from, value){return(from)}) ## 定义as<-
## setAs与setIs的区别:setIs运行后自动转化为超类,setAs需要定义def后才可在as对象时转换,且并未定义超类关系
虚拟类
定义方法:
setClass(
Class="PartitionFather",
slots=representation(nbGroups="numeric"),
contains = "VIRTUAL" )
##或
setClass(
Class="PartitionFather",
representation=representation(nbGroups="numeric","VIRTUAL"))
## 注:representation()函数中,named参数表示slot,unamed表示class
虚拟类不能创建对象,但可创建继承,继承类的对象可继承虚拟类的方法
内部修改
在定义方法setMethod
中,加入:
nameObject<-deparse(substitute(.Object))
assign(nameObject,.Object,envir=parent.frame())
可直接修改参数.Object于父环境中
编程注意事项
if 后无论是否有else的必要一定要加else,否则多个if会混淆
定义方法时最好先构造函数测试顺利后再构建
每一类包括其方法单独一个R文档