Seq ++ Option과 Option ++ Seq의 차이 Java / Scala / Groovy

++ 연산자 혹은 메서드

스칼라의 컬렉션 유형들이 제공하는 ++ 연산자를 사용하면 두 컬렉션을 손쉽게 결합할 수 있다. 이를테면 다음처럼 쓸 수 있다.

scala> Seq(1, 2, 3) ++ Seq(4, 5, 6)
res0: Seq[Int] = List(1, 2, 3, 4, 5, 6)

++는 정확히는 Seq를 이루는 여러 트레이트 가운데 하나에 선언된 메서드이다. 이 메서드는 다른 컬렉션을 인자로 받아서, 자신의 내용과 결합한 새로운 컬렉션을 반환한다.

그런데 흥미롭게도, 컬렉션이 아닌 Option에도 이 ++ 연산자를 사용할 수 있다.

scala> Option(1) ++ Option(2)
res0: Iterable[Int] = List(1, 2)

Option.++()의 미스터리

Option의 API를 살펴보면 ++ 메서드가 없다. 어떻게 된 걸까? 사실 이 경우 두 번의 암묵 변환과 하나의 암묵 인자가 적용된다.

1) Option에는 암묵 적용 함수인 option2Iterable이 선언되어 있어 Iterable로 암묵 변환될 수 있다. Iterable은 ++ 메서드가 있는 TraversableLike를 상속한다.

scala> val iter:Iterable[Int] = Option(0)
iter: Iterable[Int] = List(0)

TraversableLike.++ 메서드의 시그니처는 다음과 같다.

def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That

2) TraversableLike.++는 인자로 GenTraversableOnce를 요구한다. Iterable은 GenTraversableOnce를 상속한다. 이에 사용된 인자인 Option이 Iterable로 변환되어 ++의 인자로 삽입된다.

3) TraversableLike.++는 암묵 인자로 CanBuildFrom[Repr, B, That]을 요구한다. 이에 Iterable.canBuildFrom이 삽입된다.

4) TraversableLike.++는 암묵 인자의 That 유형 인자와 반환형이 같다. Iterable.canBuildFrom[Coll, A, Iterable[A]] 에서 That은 Iterable[A]이므로, 여기서 반환형은 Iterable[Int]이다.

암묵 변환의 효과

이렇게 Option 유형이 쉽게 Iterable로 변환되는 덕에, 다음처럼 다른 컬렉션과 쉽게 결합이 가능하다.

scala> Seq(1, 2) ++ Option(3)
res0: Seq[Int] = List(1, 2, 3)

그런데 앞서 설명한 시그니처 때문에 컬렉션의 뒤가 아니라 앞에 결합을 하려 하면 의도치 않은 반환형이 나올 수 있다. 예컨대 다음 코드에서 의도한 것은 새로운 Seq이지만 반환형은 Iterable이다.

scala> Option(1) ++ Seq(2, 3)
res0: Iterable[Int] = List(1, 2, 3)

이 경우 Seq로 변환해 Seq의 ++ 메서드를 호출하는 방법이 있다.

scala> Option(1).toSeq ++ Seq(2,3)
res0: Seq[Int] = List(1, 2, 3)

하지만 다음 메서드를 쓰면 더 간단하다.

scala> Option(1) ++: Seq(2,3)
res0: Seq[Int] = List(1, 2, 3)

++: 메서드 역시 TraversableLike에 있다. 이 메서드의 시그니처는 크게 구분되지 않지만, 그 내용을 보면 ++와는 달리 인자에 자신의 내용을 삽입한다.

Tag :
,

Leave Comments