메서드 파라미터
이름 지정(named) 파라미터
스칼라도 자바와 마찬가지로, 메서드 선언부에 적은 파라미터 순서대로 전달하는 것이 기본입니다.
def update(key: String, value: Int) =
println(s"$key -> $value")
update("x", 3)
update(key = "x", value = 3)
update(value = 3, key = "x") // 좋은 방법은 아니지만, 순서를 바꿔 쓸 수도 있습니다.
추가로, 파라미터 이름을 적어서 전달하는 방법도 있어서, 같은 타입의 파라미터가 많은 경우나, 바로 다음에 설명하는 파라미터 기본값 중, 일부를 지정할 때 쓸 수 있습니다.
파라미터 기본값
메서드에 전달할 파라미터에 기본값을 선언해 두면, 메소드를 호출할 때 해당 파라미터를 생략할 수 있습니다. 생략하면 기본으로 지정한 값이 파라미터로 전달됩니다.
def greeting(name: String, role: String = "개발자") =
println(s"안녕하세요, ${role} ${name}님.")
greeting("길동") // => 안녕하세요, 개발자 길동님.
greeting("둘리", "기획자") // => 안녕하세요, 기획자 둘리님.
위 예제에서는, name
은 평범한 파라미터로 받았고, role
은 기본값을 지정해 두었기에, 메서드를 호출할 때 생략하면, 기본값인 "개발자"가 전달됩니다.
이름 참조(by-name) 파라미터
기본 파라미터는, 파라미터 자리에 전달한 식(expression)이 먼저 평가된 다음 최종값이 메서드에 전달됩니다. 메서드 호출에 앞서서, 파라미터로 전달하는 식들이 미리 평가가 끝나는 것이죠. 반면, 이름 참조(by-name) 파라미터는, 그 자리에 전달한 식을 평가하지 않고 그대로 전달했다가, 메서드 안에서 사용하는 시점에 평가합니다.
예제로 차이점을 살펴보겠습니다.
def echoInt(n: Int): Int =
println(s"n = $n")
n
단순히 정수 Int를 받아서 프린트한 뒤, 그대로 반환하는 함수를 작성했습니다.
def ifByValue(cond: Boolean, onTrue: Int, onFalse: Int): Int =
if cond then onTrue
else onFalse
ifByValue(true, echoInt(1), echoInt(2)) // => 1반환, "n = 1", "n = 2" 둘 다 프린트
ifByValue(false, echoInt(1), echoInt(2)) // => 2반환, "n = 1", "n = 2" 둘 다 프린트
이어서, 특정 cond
의 진위에 따라, 값을 선택해 반환하는 나만의 if
메서드를 만들었습니다. 의도는 cond
조건이 참일 때는 onTrue
값을, 거짓일 때는 onFalse
값을 반환받으려 한 것입니다.
그런데 문제는, 이렇게 ifByValue
메서드를 만들어 호출하면, 참과 거짓에 해당하는 두 식 모두가 미리 평가되는 문제가 있습니다. 기본 파라미터는 메서드 호출 전에 이미 모든 파라미터에 있는 식들이 평가되기 때문입니다.
def ifByName(cond: Boolean, onTrue: => Int, onFalse: => Int): Int =
if cond then onTrue
else onFalse
ifByName(true, echoInt(1), echoInt(2)) // => 1반환, "n = 1"만 프린트
ifByName(false, echoInt(1), echoInt(2)) // => 2반환, "n = 2"만 프린트
위 ifByName
메서드 선언에서처럼, onTrue: => Int
처럼 파라미터 선언부에, =>
기호를 앞에 두면, 해당 파라미터는 이름 참조(by-name) 파라미터로 취급하며, 해당 파라미터에 위치한 식은, 메서드 안에서 쓰일 때가 돼서야 평가됩니다. 쓰이지 않는다면, 아예 평가하지 않습니다.
그래서, 첫번 째 ifByValue(True, ...)
의 경우에는, 메서드 본문에서 echoInt(1)
은 평가가 이뤄지고, echoInt(2)
의 경우에는 메서드 안에서 쓰이지 않았기 때문에, 평가되지 않습니다. 따라서, echoInt(2)
메서드 안에서 진행한 println
은 실행되지 않아서, 화면에는 n = 1
만 보이게 되는 거죠.
다중 파라미터
def normalAdder(x: Int, y: Int) = x + y
def multiAdder(x: Int)(y: Int): Int = x + y
normalAdder(2, 3) // => 5
multiAdder(2)(3) // => 5
multiAdder
메서드의 파라미터 목록을 여러 괄호쌍으로 선언하는 다중 파라미터입니다. 평소 사용할 때는, 파라미터 사이에 쉼표 대신 추가로 괄호쌍이 더해지는 모습이 조금 다를 뿐입니다.
val xs = List(1, 2, 3)
xs.map(x => normalAdder(2, x)) // => List(3, 4, 5)
xs.map(normalAdder(2, _)) // => List(3, 4, 5)
xs.map(multiAdder(2)) // => List(3, 4, 5)
다중 파라미터 메서드로, 메서드 파라미터의 일부만 적용하고 나머지 파라미터는 따로 받는, 부분 적용(partial application) 기법을 쓸 수 있습니다. 위 예제 마지막 줄에 multiAdder(2)
는 아직 y가 전달되지 않은 상태인데, 이는 곧 y
를 받아서 2 + y
를 반환하는 함수인 셈이고, xs
의 map
메서드에 함수 값으로 곧바로 전달 할 수 있습니다.