547
14
스칼라 타입 시스템 I
self
사용해서
C1
.
talk
호출한다.
c1
인스턴스를 통해
C1
.
talk
호출한다.
c1
.
c2
.
c3
통해
C3
.
talk
호출한다. 그러면 메서드는 다시
C1
.
talk
호출할 것이다.
self
라는 이름은 다른 이름을 사용해도 된다. 그것은 키워드가 아니다. 또한 필요하다면
C2
C3
안에서도 자기 타입 표기를 사용할 있다. 스크립트는 다음과 같은 출력을 보여준다.
C1
.
talk
:
Hello
C1
.
talk
:
C3
.
talk
:
World
자기 타입 지정이 없다면
C1
.
talk
C3
.
talk
내부에서 직접 호출할 있는 방법이 없다. 왜냐
하면 메서드의 이름이 같아서 후자가 전자를 가리기 때문이다. 게다가
C3
C1
직접 서브타
입도 아니기 때문에
super
.
talk
사용할 없다.
따라서 이런 경우의 자기 타입 표기는
this
일반화한참조로 생각할 있다.
14.7
구조적 타입
구조적 타입
structural
type
동적 타입 언어에서 호출할 메서드를 결정하기 위해 사용하는 방식
을 가리키는 가장 유명한 이름인 타입 지정
duck
typing
(어떤 것이 오리처럼 걷고 오리처럼
꽥거린다면, 그건 오리임에 틀림없다 )타입 안전
type
safe
버전으로 생각할 있다. 예를
루비에서 여러분 코드에
starFighter
.
shootWeapons
라는 호출이 있는데, 루비 런타임이
starFighter
인스턴스에 해당 메서드가 존재하는지 알지 못한다면, 런타임은 여러 규칙을
용해서 해당 메서드 호출을 처리하거나, 적절한 메서드가 존재하지 않을 경우를 대비한 규칙에
따라 실패를 처리한다.
스칼라는 이런 종류의 실행 시점 메서드 결정을 지원하지 않는다(가지 예외를
19
장에서
의할 것이다). 대신 스칼라는 컴파일 시 비슷한 메커니즘을 지원한다. 스칼라는 여러분이 어떤
객체가 특정 구조준수해야 한다고 지정할 있도록 해준다. 즉, 어떤 객체가 특정 멤버 (타입,
548
3
기초를 넘어서
필드, 메서드)포함해야 한다는 사실을 해당 멤버를 포함하는 타입의 이름지정하지 않고
지정할 있다.
우리는 보통 이름에 의한 타입 지정
nominal
typing
사용한다. 이는 이름을 사용해서 타입을 지정
하기 때문에 생긴 용어다. 반면 구조적 타입 지정에서는 타입의 구조에만 관심이 있다. 따라서
타입의 이름은 몰라도 된다.
Observer
패턴에서 구조적 타입을 어떻게 사용할 수 있을지 살펴보자. 앞 절에서 봤던 더 복
잡한 예제 대신
9
.
2
믹스인으로서의 트레이트에서 봤던 간단한 예제를 가지고 시작하자.
예제에서 중요한 부분만 보면 다음과 같다.
trait Observer
[
-
State
]
{
def receiveUpdate
(
state
:
State
):
Unit
}
trait Subject
[
State
]
{
private var observers
:
List
[
Observer
[
State
]]
=
Nil
...
}
구현의 단점은
Subject
상태 변경을 살펴보고 싶은 타입은 반드시
Observer
트레이트를
구현해야 한다는 있다. 하지만 실제로 최소한의 요구 사항은
receiveUpdate
메서드가 있으
된다는 것뿐이다.
그러므로 예제를
Observer
대해 구조적 타입사용하는 것으로 바꾸면 다음과 같다.
//
src
/
main
/
scala
/
progscala2
/
typesystem
/
structuraltypes
/
Observer
.
scala
package progscala2
.
typesystem
.
structuraltypes
trait Subject
{
//
import scala
.
language
.
reflectiveCalls
//
type State
//
type Observer
=
{
def receiveUpdate
(
state
:
Any
):
Unit
}
//
private var observers
:
List
[
Observer
]
=
Nil
//
549
14
스칼라 타입 시스템 I
def addObserver
(
observer
:
Observer
):
Unit
=
observers
::
=
observer
def notifyObservers
(
state
:
State
):
Unit
=
observers foreach
(
_
.
receiveUpdate
(
state
))
}
예제와 관계는 없지만
State
타입 매개변수와 추상 타입을 서로 바꿀 있음을 보여주기
위해 바꾼 부분이다.
리플렉션을 사용한 메서드 호출을 허용하기 위해 선택적인 특성을 활성화해야 한다(이어지
본문을 보라 ).
State
추상 타입
Observer
구조적 타입
Observer
에서도
State
라는 타입 매개변수를 제거했다.
type
Observer
= {
def
receiveUpdate
(
subject
:
Any
):
Unit
}
선언은 이런
receiveUpdate
메서드가 들어 있는 어떤 객체라도 관찰자로 사용할 있다는 의미다. 불행히도 스칼라에서는
구조적 타입이 추상 타입이나 타입 매개변수를 참조할 없다. 따라서
State
사용할 수 없다.
Any
같은 이미 알려진 타입을 사용해야 한다. 이는 수신자가 인스턴스를 적절한 타입으로
환해야 한다는 의미로, 상당한 단점이 아닐 없다.
임포트 문을 보면 다른 단점의 단초를 얻을 있다. 관찰자 인스턴스의 후보가 제대로 메서
드를 구현하고 있는지 타입 이름으로는 검증할 없기 때문에, 컴파일러는 리플렉션을 사용해
인스턴스에 메서드가 들어 있는지 확인할 수밖에 없다. 그에 따라 부가 비용이 든다. 물론
찰자를 자주 추가하지 않는 경우에는 이런 부가 비용을 눈치 채기 어려울 것이다. 리플렉션을
용하는 것은 선택적 기능으로 간주되기 때문에
import
사용해야 한다.
다음 스크립트에서는 새로운 구현을 사용해보자.
//
src
/
main
/
scala
/
progscala2
/
typesystem
/
structuraltypes
/
Observer
.
sc
import progscala2
.
typesystem
.
structuraltypes
.
Subject
import scala
.
language
.
reflectiveCalls
550
3
기초를 넘어서
object observer
{
//
def receiveUpdate
(
state
:
Any
):
Unit
=
println
("
got one
!
"+
state
)
}
val subject
=
new Subject
{
//
type State
=
Int
protected var count
=
0
def increment
():
Unit
=
{
count
+
=
1
notifyObservers
(
count
)
}
}
subject
.
increment
()
subject
.
increment
()
subject
.
addObserver
(
observer
)
subject
.
increment
()
subject
.
increment
()
제대로 메서드가 들어 있는 관찰자 객체를 선언한다.
Subject
트레이트를 인스턴스화하면서
State
추상 타입에 대한 정의를 제공하고 추가 동작
구현한다.
increment
호출된 다음에 관찰자를 등록했다. 따라서 오직
3
4
출력한다.
이런 단점에도 불구하고, 구조적 타입은 존재 사이의 결합을 최소화해주는 장점이 있다. 여기
그런 결합은 공유 트레이트와 같은 타입이 아니라 오직 메서드 시그니처에 의해서만 이루어
진다.
마지막으로 예제를 살펴보자. 여전히 특정 이름사용해서 결합이 이루어지는 것을
있다.
receiveUpdate
라는 메서름이 것이! 이는 어떤 의미에서는 타입 이름에
의한 결합을 메서드 이름에 의한 결합으로 옮긴 것뿐일 수도 있다. 이름은 완전히 임의로 정한
것이기 때문에, 결합을 없애는 것을 한 단계 진행해서
Observer
인자가 하나뿐인 함수에
대한 별명이 되도록 정의할 수도 있다. 다음은 그에 따라 만들어진, 우리 예제의 마지막 형태다.

Get 프로그래밍 스칼라: 실용적인 스칼라 활용법을 익히는 가장 확실한 실전 바이블 (2.11.x 버전 기반) now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.