> getAnywhere(print.xtabs) A single object matching ‘print.xtabs’ was found It was found in the following places registered S3 method for print from namespace stats namespace:stats with value
person <- function(x, ...) { UseMethod('person') }
person.father <- function(x, ...) { print("I am father.") }
person.son <- function(x, ...) { NextMethod() print("I am son.") }
执行
1 2 3 4 5 6 7
> p1 <- structure(1,class=c("father")) > person(p1) [1] "I am father." > p2 <- structure(1,class=c("son","father")) > person(p2) [1] "I am father." [1] "I am son."
> tom <- new("Person",name="tom",age=18) > tom An object of class "Person" Slot "name": [1] "tom"
Slot "age": [1] 18
也可以使用另一种方式
1 2 3 4 5 6 7 8 9 10
> Person <- setClass("Person",slots=list(name="character",age="numeric")) > tom <- Person(name="tom", age=18) > tom An object of class "Person" Slot "name": [1] "tom"
> new("Person") An object of class "Person" Slot "name": [1] "Unknow"
Slot "age": [1] 18
> sam <- new("Person",name="sam") > sam An object of class "Person" Slot "name": [1] "sam"
Slot "age": [1] 18
2.4 类型检查
在上面 Person 类的定义中,我们指定了属性值的类型,如果我们传入的类型不一致会是什么结果呢?
1 2 3
> new("Person", name="tom", age="0") Error in validObject(.Object) : 类别为“Person”的对象不对: invalid object for slot "age" in class "Person": got class "character", should be or extend class "numeric"
> setClass("Person",slots=list(name="character",age="numeric")) > tom <- new("Person",name="tom",age=18) > jay <- initialize(tom, name="jay", age=20) > jay An object of class "Person" Slot "name": [1] "jay"
Slot "age": [1] 20
> tom An object of class "Person" Slot "name": [1] "tom"
> genders <- factor(c("F", "M")) > tom <- Person$new(name="tom", age=19, gender=genders[1]) # tom <- Person(name="tom", age=19, gender=genders[1]) > tom Reference class object of class "Person" Field "name": [1] "tom" Field "age": [1] 19 Field "gender": [1] F Levels: F M
> sam <- tom > sam Reference class object of class "Person" Field "name": [1] "tom" Field "age": [1] 20 Field "gender": [1] F Levels: F M > sam$name <- "sam" > sam Reference class object of class "Person" Field "name": [1] "sam" Field "age": [1] 20 Field "gender": [1] F Levels: F M > tom Reference class object of class "Person" Field "name": [1] "sam" Field "age": [1] 20 Field "gender": [1] F Levels: F M
> sam <- tom$copy() > sam$age <- 28 > sam Reference class object of class "Person" Field "name": [1] "sam" Field "age": [1] 28 Field "gender": [1] F Levels: F M > tom Reference class object of class "Person" Field "name": [1] "sam" Field "age": [1] 20 Field "gender": [1] F Levels: F M
3.4 定义方法
在 RC 对象系统中,我们可以在创建类的同时指定对应的方法,而不需要将类和函数的定义分离。
例如,我们定义 Person 类
1 2 3 4 5 6 7 8 9
Person <- setRefClass("Person", fields = c(name='character',age='numeric',gender='factor'), methods = list( setName = function(x) { name <<- x }, setAge = function(x) { age <<- x } ))
然后使用方法,改变属性值
1 2 3 4 5 6 7 8 9 10 11
> jay <- Person(name="jay", age=21, gender=genders[2]) > jay$setAge(28) > jay Reference class object of class "Person" Field "name": [1] "jay" Field "age": [1] 28 Field "gender": [1] M Levels: F M
Person <- setRefClass("Person", fields = c(name='character',age='numeric',gender='factor'), methods = list( setName = function(x) { name <- x }, setAge = function(x) { age <- x } ))
执行相同的代码
1 2 3 4 5 6 7 8 9 10 11
> jay <- Person(name="jay", age=21, gender=genders[2]) > jay$setAge(28) > jay Reference class object of class "Person" Field "name": [1] "jay" Field "age": [1] 21 Field "gender": [1] M Levels: F M
发现 age 的值并没有被修改
为了不让类定义看起来很臃肿,可以将函数的定义剥离出来。
我们可以使用 $methods() 的方式为类定义相应的方法。
1 2 3 4 5 6 7 8 9 10
Person <- setRefClass("Person", fields = c(name='character',age='numeric',gender='factor'))
Person$methods( setName = function(x) { name <<- x }, setAge = function(x) { age <<- x } )
调用方法
1 2 3 4 5 6 7 8 9 10 11
> jay <- Person(name="jay", age=21, gender=genders[2]) > jay$setAge(28) > jay Reference class object of class "Person" Field "name": [1] "jay" Field "age": [1] 28 Field "gender": [1] M Levels: F M
Person <- setRefClass("Person", fields = c(name='character',age='numeric',gender='factor'), methods = list( initialize = function(name="Unknown", age=18, gender=genders[1]) { name <<- name age <<- age gender <<- gender } ))
实例化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
> Person() Reference class object of class "Person" Field "name": [1] "Unknown" Field "age": [1] 18 Field "gender": [1] F Levels: F M > Person(name="tom") Reference class object of class "Person" Field "name": [1] "tom" Field "age": [1] 18 Field "gender": [1] F Levels: F M
tom <- VIP(username="tom", password="123456", level=3) > tom Reference class object of class "VIP" Field "username": [1] "tom" Field "password": [1] "123456" Field "level": [1] 3 > tom$getName() [1] "tom"
3.7 RC 的内置方法
在我们定义完类之后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
> Person Generator for class "Person":
Class fields: Name: name age gender Class: character numeric factor
> tom <- VIP(username="tom", password="123456", level=3) > tom$getName() VIP: tom
我们重新为 tom 的属性赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
> tom$initFields(username="sam", password="1234") Reference class object of class "VIP" Field "username": [1] "sam" Field "password": [1] "1234" Field "level": [1] 3 > tom Reference class object of class "VIP" Field "username": [1] "sam" Field "password": [1] "1234" Field "level": [1] 3
获取或设置某一属性
1 2 3 4 5 6 7 8 9 10 11
> tom$field("username") [1] "sam" > tom$field("username", "tom") > tom Reference class object of class "VIP" Field "username": [1] "tom" Field "password": [1] "1234" Field "level": [1] 3
> tom$show() Reference class object of class "VIP" Field "username": [1] "tom" Field "password": [1] "1234" Field "level": [1] 3 > show(tom) Reference class object of class "VIP" Field "username": [1] "tom" Field "password": [1] "1234" Field "level": [1] 3
追踪方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
> tom$trace("add") Tracing reference method "add" for object from class "VIP" [1] "add" > tom$add(1, 3) Tracing tom$add(1, 3) on entry [1] 4 > tom$add(4, 5) Tracing tom$add(4, 5) on entry [1] 9 > tom$untrace("add") Untracing reference method "add" for object from class "VIP" [1] "add" > add(1, 1) [1] 2
将子类转换为父类类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14
> tom$export('User') Reference class object of class "User" Field "username": [1] "tom" Field "password": [1] "1234" > tom Reference class object of class "VIP" Field "username": [1] "tom" Field "password": [1] "1234" Field "level": [1] 3
> sam <- VIP() > sam Reference class object of class "VIP" Field "username": character(0) Field "password": character(0) Field "level": numeric(0) > sam$import(tom$export("User")) > sam Reference class object of class "VIP" Field "username": [1] "tom" Field "password": [1] "1234" Field "level": numeric(0) > sam$import(tom) > sam Reference class object of class "VIP" Field "username": [1] "tom" Field "password": [1] "1234" Field "level": [1] 3
> User$lock("username") # 查看被锁定的属性 > User$lock() [1] "username" > a <- User(username="fly") > a Reference class object of class "User" Field "username": [1] "fly" Field "password": character(0) > a$username <- "a" 错误: invalid replacement: reference class field ‘username’ is read-only
> a <- American("lusy", 20) > a Reference class object of class "American" Field "name": [1] "lusy" Field "age": [1] 20 Field "language": [1] "english" > a$talk() [1] "talking...english" > b <- Chinese("lisin", 18) > b Reference class object of class "Chinese" Field "name": [1] "lisin" Field "age": [1] 18 Field "language": [1] "chinese" > b$talk() [1] "talking...chinese"
Person <- R6Class( "Person", public = list( name = NA, initialize = function(name) { self$name <- name }, say = function() { cat("my name is ", self$name) } ) )
创建实例,同样使用 $new 方法来实例化
1 2 3 4 5 6 7 8
> tom <- Person$new(name = "tom") > tom <Person> Public: clone: function (deep = FALSE) initialize: function (name) name: tom say: function ()
Person <- R6Class( "Person", public = list( name = NA, initialize = function(name, money) { self$name <- name private$money <- money }, say = function() { cat("my name is ", self$name) }, incSalary = function(percent) { private$setMoney(private$money * (1 + percent)) invisible(self) } ), private = list( money = NA, setMoney = function(m) { cat(paste0("change ", self$name, "'s salary!\n")) private$money <- m } ) )
我们添加了私有属性 money 和私有函数 setMoney
我们先创建一个实例化对象
1 2 3 4 5 6 7 8 9 10 11 12
> tom <- Person$new(name = "tom", 1000) > tom <Person> Public: clone: function (deep = FALSE) incSalary: function (percent) initialize: function (name, money) name: tom say: function () Private: money: 1000 setMoney: function (m)
Student <- R6Class( "Student", public = list( name = NA, initialize = function(name) { self$name <- name } ), private = list( .score = NA ), active = list( score = function(s) { if (missing(s)) return(private$.score) if (s < 0 || s > 100) stop("Score incorrect!") private$.score <- s } ) )
_注意_:public, private 和 active 参数内的属性名必须唯一,所以我们将私有属性改为了 .score
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
> sam <- Student$new("sam") > sam <Student> Public: clone: function (deep = FALSE) initialize: function (name) name: sam score: active binding Private: .score: NA > sam$score [1] NA > sam$score <- 100 > sam$score [1] 100
4.5 继承
R6 通过 inherit 参数指定父类,例如,我们定义一个 worker 类,它继承自上面的 Person 类
1 2 3 4 5 6 7 8 9 10
Worker <- R6Class( "Worker", inherit = Person, public = list( company = "Gene", info = function() { print("NGS analysis!") } ) )
创建对象并使用父类的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
> siri <- Worker$new("Siri", 100) > siri$incSalary(0.1) change Siri's salary! > siri <Worker> Inherits from: <Person> Public: clone: function (deep = FALSE) company: Gene incSalary: function (percent) info: function () initialize: function (name, money) name: Siri say: function () test: function () Private: money: 110 setMoney: function (m)
我们可以使用 super 对象来调用父类的方法,让我们来重写 incSalary 方法
1 2 3 4 5 6 7 8 9 10 11 12 13
Worker <- R6Class( "Worker", inherit = Person, public = list( company = "Gene", info = function() { print("NGS analysis!") }, incSalary = function(percent) { super$incSalary(percent + 0.1) } ) )
运行与上面相同的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
> siri <- Worker$new("Siri", 100) > siri$incSalary(0.1) change Siri's salary! > siri <Worker> Inherits from: <Person> Public: clone: function (deep = FALSE) company: Gene incSalary: function (percent) info: function () initialize: function (name, money) name: Siri say: function () test: function () Private: money: 120 setMoney: function (m)
> Student$set("public", "age", 21) > sam <- Student$new("sam") > sam <Student> Public: age: 21 clone: function (deep = FALSE) initialize: function (name) name: sam score: active binding Private: .score: NA
当然也可以使用这种方式修改属性值
1 2 3 4 5 6 7 8 9 10 11 12
> Student$set("public", "age", 18, overwrite = TRUE) > sam <- Student$new("sam") > sam <Student> Public: age: 18 clone: function (deep = FALSE) initialize: function (name) name: sam score: active binding Private: .score: NA