[Scala] implicit

smlee·2023년 8월 29일
0

Scala

목록 보기
28/37
post-thumbnail

Akka를 공부하다가 예제에서 implicit라는 키워드를 사용하는 것을 보았다.
이 키워드가 정확하게 의미하는 것이 무엇인지 몰랐다. 따라서 implicit에 대해 정리하려고 한다.

또한, 이 내용은 Programming in Scala 4/e Chapter.21 - 암시적 변환과 암시적 파라미터에 자세하게 나와있어 이 내용들을 종합하여 정리할 예정이다.


스칼라에서 implicit는 매우 편리하기도 하지만 코드 가독성을 엄청 떨어뜨릴 수도 있는 키워드이다.

보통 사람들은 자신의 코드는 마음대로 바꾸거나 확장하며 사용하지만, 다른 사람이 만든 라이브러리를 사용하고 싶은 경우에는 있는 그대로 사용한다. 스칼라에서는 이러한 문제에 해결 방법으로 암시적 파라미터와 암시적 변환을 사용한다.

이 포스트에서는 위의 내용들을 정리하려고 한다.

암시적 변환(implicit conversion)

암시적 변환은 서로를 고려하지 않고 독립적으로 개발된 두 덩어리의 서프트웨어를 한데 묶을 때 유용하다. 즉, 근본적으로 동일한 어떤 대상을 각 라이브러리가 각각 다르게 인코딩할 수 있다.

다음 예제를 보자.

val button = new JButton

button.addActionListener(
	new ActionListener {
    	def actionPerformed(event:ActionEvent) = println("pressed!")
    }
)

위의 코드는 addActionListener가 내부 인자로 ActionListener라는 객체를 원하고 있고, 해당 객체 안에 들어있는 함수를 선언한 것이다.

물론 제대로 의미가 전달되지만 스칼라스러운 코드는 아니다.

button.addActionListener( (_:ActionEvent) => println("pressed") )

그럼 위와 같은 스칼라스러운 코드는 잘 동작할까? 정답은 아니다. [나중에 정리하겠지만 스칼라 2.12에서는 위의 코드가 실행된다.]
왜냐하면 addActionListener가 파라미터로 원하는 것은 함수가 아니라 객체이다. 하지만, 암시적 변환을 사용한다면 이 코드를 작동하게 할 수 있다.

implicit def function2ActionListener(f:ActionEvent => Unit) =
	new ActionListener {
    	def actionPerformed(event:ActionEvent) => f(event)
    }
    
button.addActionListener(
	function2ActionListener(
    	(_:ActionEvent) => println("pressed")
    )
)

위의 코드는 잘 동작한다.

implicit def function2ActionListener(f:ActionEvent => Unit) =
	new ActionListener {
    	def actionPerformed(event:ActionEvent) => f(event)
    }
    
button.addActionListener(
	(_:ActionEvent) => println("pressed")
)

또한 위의 코드 역시 잘 동작한다. 그 이유는 무엇일까?
function2ActionListenerimplicit로 표시했으므로 이를 생략해도 암시적 변환을 통해 위의 코드를 제대로 동작 시킨다.

예제를 하나만 더 보자.

case class Paper(content: String)
case class Book(name: String, code: Int)

// book 객체를 paper 객체로 바꾸는 메소드
implicit def bookToPaper(book: Book): Paper = Paper(s"${book.code} - ${book.name}")

val book: Book = Book("Scala", 1234)
val paper: Paper = book // bookToPaper 함수를 이용해 Paper 객체로 변환

분명, Book에서 Paper로 바꾸려면 bookToPaper라는 메소드가 사용되어야 할텐데, 메소드가 없이도 잘 변환이 된다. 즉, implicit로 선언된 형변환 메소드를 통해 오토 캐스팅이 가능해진다.

문제점

하지만 이렇게 편리한 기능을 제공하는 암시적 변환도 다음과 같은 문제점이 있다.

  1. implicit로 선언된 것은 지역적이지가 않다. 즉, 접근제어자를 통해 클래스나 패키지 제한을 걸어두어도 프로젝트에 존재하면 그냥 사용할 수 있는 것이다.
  2. 컴파일이 느려진다 -> implicit로 선언된 값을 찾기 위한 검색 시간이 소요되므로 컴파일 시간이 증가한다.

암시적 파라미터

암시적 파라미터는 메서드의 파라미터에 implicit를 선언하는 방식이다.
컴파일러는 때때로 someCall(a) 호출을 내부에서 someCall(a)(b)로 바꾸거나, new SomeClass(a)new SomeClass(a)(b)로 자체적으로 바꾸기도 한다. 즉, 함수 호출을 완성하는데 필요한 빠진 파라미터 목록을 채워 넣어주는 역할도 한다.

위와 같이 사용하기 위해서는 디폴트 값이 있거나 implicit를 표시하여 어떤 파라미터에 채울 것인지 명시한다.

implicit val x: Int = 500

def plusValue(paramX: Int)(implicit paramY: Int) = paramX + paramY
println( plusValue(100)(200) ) // 300
println( plusValue(200) ) // 700

위의 예시를 보자. 위의 예시에서 implicit로 x가 선언되었으며, plusValue의 파라미터 목록 중 paramY가 implicit로 선언되었다. 즉, 만약 파라미터가 빠져있다면 implicit로 선언된 paramY 자리에 외부 스코프에 있는 implicit 값을 가져오게 한다는 소리이다.

Reference

0개의 댓글