일반 클래스는 기본적으로 AnyRef를 상속받는다. 사실 extends가 있으나 없으나 동일하다. 자바의 Object와 동일하기 때문이다.
간단한 케이스 클래스 예시로 이해해보자.
case class Man(name: String) extends AnyRef {
def canEqual(that: Any): Boolean = that.isInstanceOf[Man]
override def equals(obj: scala.Any): Boolean = super.equals(obj)
override def hashCode(): Int = super.hashCode()
}
object Main extends App {
var i: AnyRef = new AnyRef
println(i)
val samuel = Man("Samuel")
val matt = Man("Matt")
println(samuel.equals(matt))
println(samuel.canEqual(matt))
println(samuel.hashCode)
}
AnyRef 객체를 출력하면, Object가 출력된다. Man 클래스는 AnyRef에 있는 메소드 일부를 override해서 사용했다.
결과는 다음과 같다.
java.lang.Object@3a03464
false
true
759156157
이제 커링을 살펴본다.
커링은 타입 추론을 지원하는 스칼라의 함수 기능이다.
여러 개의 매개변수가 가지고 있는 함수를
매개변수가 하나인 함수로 나눌 수 있다.
f (a, b) = c라는 함수 f가 있다고 가정하자.
F (a) = G, G(b) = c 가 되는 함수로 만들 수 있다. 이 때 함수 F를 f의 커리라 한다.
object Numbers {
def add(a: Int, b: Int) : Int = a + b
def addCurrying1(a: Int)(b: Int) : Int = a + b
def addCurrying2(a: Int) : Int => Int = b => a + b
def addCurrying3(a: Int) = (b: Int) => a + b
}
import com.google.Numbers._
println(add(1, 2))
println(addCurrying1(1)(2))
println(addCurrying2(1)(2))
println(addCurrying3(1)(2))
println(addCurrying1 _)
println(addCurrying1(1) _)
println(addCurrying2 _) // <function1> 출력
//println(addCurrying2(1) _)
//Error:(31, 23) _ must follow method; cannot follow Int => Int
println(addCurrying3 _) // <function1> 출력
//println(addCurrying3(1) _)
//Error:(31, 23) _ must follow method; cannot follow Int => Int
//val f1 = addCurrying1(1)
// Error:(27, 24) missing argument list for method addCurrying1 in object Numbers
//Unapplied methods are only converted to functions when a function type is expected.
//You can make this conversion explicit by writing `addCurrying1 _` // or `addCurrying1(_)(_)` instead of `addCurrying1`.
val f2 = addCurrying2(1)
val f3 = addCurrying3(1)
val g2 = f2(2)
val g3 = f3(2)
assert(g2 == 3)
assert(g3 == 3)
override /*IterableLike*/
def foldRight[B](z: B)(@deprecatedName('f) op: (A, B) => B): B =
if (this.isEmpty) z
else op(head, tail.foldRight(z)(op))
override /*TraversableLike*/
def reduceLeft[B >: A](f: (B, A) => B): B =
if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft")
else tail.foldLeft[B](head)(f)
override /*IterableLike*/
def reduceRight[B >: A](op: (A, B) => B): B =
if (isEmpty) throw new UnsupportedOperationException("Nil.reduceRight")
else if (tail.isEmpty) head
else op(head, tail.reduceRight(op))
또한 retry하는 대표적인 예제에도 커링이 들어간다.
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e if n > 1 =>
retry(n - 1)(fn)
}
}
특화(specialization)은 제네릭 사용시 type erasure로 인해 boxing, unboxing이 발생할 수 있다. 예를 들어, fill 메소드에 엄청큰 수를 넣고 map 함수를 사용하면(에, List.fill(50000)(2).map(_* 3) ), 엄청난 boxing, unboxing이 일어나 성능이 저하될 수 있다. 이를 방지하기 위해서 나온 개념이다.
간단한 예시를 살펴본다.
case class Count[T](value: T)
object Main extends App {
def newCount(l: Long): Count[Long] = Count(l)
}
별 것도 아닌 것처럼 보이지만, scalac -print로 확인해보면, 박싱을 진행하고 있다.
def newCount(l: Long): Count = new Count(scala.Long.box(l));
특화를 적용(@specialized 추가)하면 다음과 같다.
case class Count[@specialized(Long, Int) T](value: T)
object Main extends App {
def newCount(l: Long): Count[Long] = Count(l)
}
scalac -print로 살펴보면, 클래스가 생겼고, boxing이 없는 코드로 바뀌어 있다.
def newCount(l: Long): Count = new Count$mcJ$sp(l);
<specialized> class Count$mcI$sp extends Count {
<paramaccessor> <specialized> protected[this] val value$mcI$sp: Int = _;
<stable> <accessor> <specialized> def value$mcI$sp(): Int = Count$mcI$sp.this.value$mcI$sp;
override <stable> <accessor> <specialized> def value(): Int = Count$mcI$sp.this.value$mcI$sp();
override <synthetic> <specialized> def copy$default$1(): Int = Count$mcI$sp.this.copy$default$1$mcI$sp();
override <synthetic> <specialized> def copy$default$1$mcI$sp(): Int = Count$mcI$sp.this.value();
def specInstance$(): Boolean = true;
override <synthetic> <bridge> <specialized> <artifact> def copy$default$1(): Object = scala.Int.box(Count$mcI$sp.this.copy$default$1());
override <stable> <bridge> <specialized> <artifact> def value(): Object = scala.Int.box(Count$mcI$sp.this.value());
<specialized> def <init>(value$mcI$sp: Int): Count$mcI$sp = {
Count$mcI$sp.this.value$mcI$sp = value$mcI$sp;
Count$mcI$sp.super.<init>(null);
()
}
};
'scala' 카테고리의 다른 글
[scala] class 4 - 타입 바운드(type bounds), 타입 변성(type variance) (0) | 2016.09.26 |
---|---|
[scala] first class (0) | 2016.09.26 |
[scala] class 3 - 믹스인(mixin) 클래스, AnyVal, 유니버셜 트레이트(trait), abstract type (0) | 2016.09.22 |
[scala] scala 코드 역어셈블링하기 (부제 : scala 코드 분석하기) (0) | 2016.09.22 |
[scala] class 예시 2 - 암시적 변환(implicit conversion - implicit converter/implicit class/implicit parameter) (0) | 2016.09.21 |