388
2
부
기본기 다지기
해준다는 것도 배웠다. 구현하는 방법을 보지 못한 연산자가 하나 있는데, 바로 단항 연산자
unary
operator
다.
예제는 음수 연산이다. 복소수 클래스를 정의하는 경우
-
c
와 같은 어떤 인스턴스의 음수를 어떻
게 지원할 수 있을까? 다음은 그 방법을 보여준다.
//
src
/
main
/
scala
/
progscala2
/
basicoop
/
Complex
.
sc
case class Complex
(
real
:
Double
,
imag
:
Double
)
{
def unary
_
-
:
Complex
=
Complex
(
-
real
,
imag
)
//
➊
def
-
(
other
:
Complex
)
=
Complex
(
real
-
other
.
real
,
imag
-
other
.
imag
)
}
val c1
=
Complex
(
1
.
1
,
2
.
2
)
val c2
=
-
c1
//
Complex
(
-
1
.
1
,
2
.
2
)
val c3
=
c1
.
unary
_
-
//
Complex
(
-
1
.
1
,
2
.
2
)
val c4
=
c1
-
Complex
(
0
.
5
,
1
.
0
)
//
Complex
(
0
.
6
,
1
.
2
)
➊ 메서드 이름이
unary
_
X
다. 여기서
X
는 우리가 사용하고 싶은 연산자 문자로, 여기서는
-
다.
-
와
:
사이에 공백이 있음에 유의하라. 이 공백은 메서드 이름이
-
로 끝나고,
:
로 끝나지 않음을
컴파일러에 알려주기 위한 것이다! 비교를 위해 일반적인 뺄셈 연산자도 구현했다.
단항 연산자를 정의하고 나면,
c2
를 정의할 때처럼 이를 인스턴스 앞에 위치시킬 수 있다. 또한
c3
에서처럼 다른 메서드와 같이 호출할 수도 있다.
8.7
입력 검증하기
입력 인자를 검증해서 결과 인스턴스가 정상적인 상태임을 보장하고 싶다면 어떻게 해야 할까?
Predef
(
http
://
bit
.
ly
/
1086O2z
)에는 이런 경우에 사용할 수 있는
require
라는 이름의 오버
로딩한 메서드 집합이 있다. 미국 우편번호를 저장하는 클래스를 생각해보자.
5
자리 숫자로 구
성된 형식과 뒤에 추가로
4
자리 숫자가 더 오는 ‘
zip
+
4
’ 형식, 이렇게 두 가지 형식이 가능하
다. 이 형식은 보통 ‘
12345
-
6789
’라는 식으로 쓰인다. 또한 모든 수가 실제 우편번호인 것은 아
니다.
389
8
장
스칼라 객체지향 프로그래밍
//
src
/
main
/
scala
/
progscala2
/
basicoop
/
Zipcode
.
scala
package progscala2
.
basicoop
case class ZipCode
(
zip
:
Int
,
extension
:
Option
[
Int
]
=
None
)
{
require
(
valid
(
zip
,
extension
),
//
➊
s
"
Invalid Zip
+
4 specified
:
$
toString
")
protected def valid
(
z
:
Int
,
e
:
Option
[
Int
]):
Boolean
=
{
if
(
0
<
z
&&
z
<
=
99999
)
e match
{
case None
=
>
validUSPS
(
z
,
0
)
case Some
(
e
)
=
>
0
<
e
&&
e
<
=
9999
&&
validUSPS
(
z
,
e
)
}
else false
}
/**
실제
미국에서
사용
중인
우편번호인가
?
*/
protected def validUSPS
(
i
:
Int
,
e
:
Int
):
Boolean
=
true
//
➋
override def toString
=
//
➌
if
(
extension
!
=
None
)
s
"$
zip
-
${
extension
.
get
}"
else zip
.
toString
}
object ZipCode
{
def apply
(
zip
:
Int
,
extension
:
Int
):
ZipCode
=
new ZipCode
(
zip
,
Some
(
extension
))
}
➊
require
메서드를 사용해서 입력을 검증한다.
➋ 실제 구현에서는
USPS
(미국 우체국)에서 제공하는 데이터베이스를 사용해서 우편번호가
실제로 존재하는지 검증해야 할 것이다.
➌
toString
을 오버라이딩해서, 사람들이 우편번호라고 예상하는 형식으로 표시한다. 이때 추
가
4
자리 확장도 제대로 처리한다.
다음은 이 클래스를 사용하는 스크립트다.
390
2
부
기본기 다지기
//
src
/
main
/
scala
/
progscala2
/
basicoop
/
Zipcode
.
sc
import progscala2
.
basicoop
.
ZipCode
ZipCode
(
12345
)
//
결과
:
ZipCode
=
12345
ZipCode
(
12345
,
Some
(
6789
))
//
결과
:
ZipCode
=
12345
-
6789
ZipCode
(
12345
,
6789
)
//
결과
:
ZipCode
=
12345
-
6789
try
{
ZipCode
(
0
,
6789
)
//
잘못된
zip
+
4
형식의
우편번호
:
0
-
6789
}
catch
{
case e
:
java
.
lang
.
IllegalArgumentException
=
>
e
}
try
{
ZipCode
(
12345
,
0
)
//
잘못된
zip
+
4
형식의
우편번호
:
12345
-
0
}
catch
{
case e
:
java
.
lang
.
IllegalArgumentException
=
>
e
}
값을 생성할 때 검증을 한 번만 수행해서,
ZipCode
의 인스턴스를 사용하는 사용자들이 더 이상
의 검증이 필요하지 않다고 알도록 하는 것이,
ZipCode
와 같이 분야에 특화된 타입을 정의해야
하는 좋은 이유다.
Predef
에는 비슷한 목적의
ensuring
과
assume
이라는 메서드도 있다. 이 두 메서드와
require
단언문
assertion
메서드의 사용법을
23
.
5
절 ‘계약에 의한 설계를 활용해서 더 좋게 설계하기’에서
살펴볼 것이다.
지금은 생성 단계에서 검증을 수행하는 것에 대해 다뤘지만, 이 단언문 메서드는 어떤 메서드 안
에서든 호출 가능하다. 하지만 값 클래스의 본문은 예외다. 단언문을 그 안에서 사용할 수 없다.
만약 단언문을 사용한다면 힙에 객체를 할당해야 할 것이다. 하지만 생성자가 두 번째 인자를 취
하기 때문에 어차피
ZipCode
는 값 클래스일 수 없다.
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.