17.4 상속
이 절에서는 R의 상속에 대한 모든 것을 살펴 보겠습니다. 보다 구체적으로는 S3
, S4
그리고 참조 클래스에서 상속을 생성하고이를 프로그램에서 효율적으로 사용하는 방법을 알아 보겠습니다.
상속(inheritance)은 기존 클래스에서 새 클래스를 정의할 수있는 개체 지향 프로그래밍의 주요 기능 중 하나입니다. 즉, 기존 기본 클래스에서 새 클래스를 파생시키고 새 기능을 추가할 수 있습니다. 처음부터 쓸 필요가 없습니다. 따라서 상속은 코드의 재사용성(reusability)을 제공합니다.
상속은 가계도처럼 클래스의 계층을 형성합니다. 중요한 점은 기본 클래스에 대해 정의된 특성이 파생 클래스에 자동으로 존재한다는 것입니다.
또한 기본 클래스의 메소드는 파생된 클래스에서도 작동합니다.
아래에서는 R 프로그래밍 언어의 세 가지 클래스 시스템에 대해 상속이 수행되는 방법에 대해 설명합니다.
17.4.1 S3
클래스의 상속
S3
클래스에는 고정된 정의가 없습니다. 따라서 S3
객체의 속성은 임의적일 수 있습니다.
그러나 파생된 클래스는 기본 클래스에 대해 정의된 메소드를 상속합니다. 다음과 같이 stu
클래스의 새로운 개체를 생성하는 함수가 있다고 가정해 봅니다.
<- function(n,a,g) {
stu <- list(name=n, age=a, GPA=g)
value attr(value, "class") <- "stu"
value }
또한 다음과 같이 일반 함수 print()
에 대해 정의된 메소드가 있습니다.
<- function(obj) {
print.stu cat(obj$name, "\n")
cat(obj$age, "years old\n")
cat("GPA:", obj$GPA, "\n")
}
print.stu(s3.s1)
## Paul
## 26 years old
## GPA: 3.7
s3.s1
## Paul
## 26 years old
## GPA: 3.7
이제 stu
에서 상속하는InternationalStudent
클래스의 개체를 만들고 싶습니다.
이것은 class(obj) <- c(child, parent)
와 같은 클래스 이름의 문자형 벡터를 할당하여 수행됩니다.
# 리스트를 생성합니다.
<- list(name="John", age=21, GPA=3.5, country="France")
s3.s2 s3.s2
## $name
## [1] "John"
##
## $age
## [1] 21
##
## $GPA
## [1] 3.5
##
## $country
## [1] "France"
class(s3.s2)
## [1] "list"
# stu 클래스에서 파생된 InternationalStudent 클래스를 만듭니다.
class(s3.s2) <- c("InternationalStudent", "stu")
class(s3.s2)
## [1] "InternationalStudent" "stu"
# 개체를 출력합니다.
print.stu(s3.s2)
## John
## 21 years old
## GPA: 3.5
s3.s2
## John
## 21 years old
## GPA: 3.5
print.InternationalStudent()
형식의 메소드를 정의하지 않았으므로 print.stu()
메서드가 호출되었음을 위에서 볼 수 있습니다. 이 class 메소드는 상속된 것입니다.
이제 print.InternationalStudent()
를 정의하겠습니다.
<- function(obj) {
print.InternationalStudent cat(obj$name, "is from", obj$country, "\n")
}
아래와 같이 stu
클래스에 대해 정의된 메소드를 덮어 씁니다.
s3.s2
## John is from France
inherits()
함수 또는 is()
함수와 같은 함수들을 이용하여 상속 여부를 확인할 수 있습니다.
inherits(s3.s2,"stu")
## [1] TRUE
is(s3.s2,"stu")
## [1] TRUE
17.4.2 S4
클래스의 상속
S4
클래스에는 적절한 정의가 있으므로 파생 클래스는 부모 클래스의 특성과 메소드를 모두 상속합니다.
일반 함수 show()
에 대한 메소드로 student1
클래스를 정의해 보겠습니다.
# student1라 불리는 클래스를 정의합니다.
setClass("student1", slots=list(name="character", age="numeric", GPA="numeric"))
# show() 일반 함수를 위한 클래스 메소드를 정의합니다.
setMethod("show", "student1", function(object) {
cat(object@name, "\n")
cat(object@age, "years old\n")
cat("GPA:", object@GPA, "\n")
} )
상속은 다음과 같이 인수가 포함된 파생 클래스 정의 중에 수행됩니다.
# student1에서 상속됩니다.
setClass("InternationalStudent",
slots=list(country="character"),
contains="student1"
)
여기에 country
속성을 추가했으며 나머지는 부모로부터 상속됩니다.
<- new("InternationalStudent", name="John", age=21, GPA=3.5, country="France")
s3 s3
## John
## 21 years old
## GPA: 3.5
show(s3)
## John
## 21 years old
## GPA: 3.5
우리가 show(s)
를 했을 때 student
클래스에 대한 정의가 호출된 것을 볼 수 있습니다.
S3
시스템의 경우처럼 기본 클래스의 메소드를 덮어 쓰는 파생 클래스에 대한 메소드를 정의 할 수 있습니다.
17.4.3 참조 클래스의 상속
참조 클래스의 상속은S4
클래스의 상속과 매우 유사합니다. 파생할 기본 클래스를 contains
인수에서 정의합니다.
다음은 inc_age()
와 dec_age()
두 가지 메소드가 있는 st1
참조 클래스의 예입니다.
<- setRefClass("ref.st",
ref.st fields=list(name="character", age="numeric", GPA="numeric"),
methods=list(
inc_age = function(x) {
<<- age + x
age
},dec_age = function(x) {
<<- age - x
age
}
) )
이제 이 클래스에서 상속합니다. 또한dec_age()
메소드를 덮어 써서 age
가 음수가 되지 않도록 무결성 검사를 추가합니다.
<- setRefClass("IntStu",
IntStu fields=list(country="character"),
contains="ref.st",
methods=list(
dec_age = function(x) {
if((age - x) < 0) stop("Age cannot be negative")
<<- age - x
age
}
) )
테스트 해 보겠습니다.
<- ref.st(name="Paul", age = 18, GPA = 3.8)
ref.s4
$age ref.s4
## [1] 18
$dec_age(5)
ref.s4$age ref.s4
## [1] 13
<- IntStu(name="John", age=21, GPA=3.5, country="France")
ref.s5
$age ref.s5
## [1] 21
$inc_age(5)
ref.s5$age ref.s5
## [1] 26
$dec_age(5)
ref.s5$age ref.s5
## [1] 21
이런 식으로 부모 클래스에서 상속할 수 있습니다.