Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
language:scala [2021/02/01 19:18] – removed ledyxlanguage:scala [2021/03/24 08:18] (current) – [Command Line] 프로젝트 생성/배포 내용 보강 ledyx
Line 1: Line 1:
 += Scala =
 +
 +* https://docs.scala-lang.org/
 +* 기본적으로 Java를 다뤄봤다는 전제하에 실용적인 측면으로만 기술.
 +* 2.12 이상 버전을 기준으로 기술.
 +
 +{{tag>Language Scala JVM Object_Oriented_Programming Functional_Programming}}
 +
 += 환경 설정 =
 +* JDK 1.8 이상 설치
 +
 +== Command Line ==
 +https://www.scala-lang.org/download/
 +
 +=== SBT (Scala Build Tools) ===
 +Scala의 Ant/Maven/Gradle와 비교될 수 있음. 직접으로 프로젝트 생성, 배포 관여.
 +
 +==== Create templates ====
 +
 +https://www.scala-sbt.org/1.x/docs/sbt-new-and-Templates.html
 +
 +<sxh shell>
 +sbt new scala/scala-seed.g8
 +</sxh>
 +
 +
 +==== Fat Jar ====
 +
 +https://github.com/sbt/sbt-assembly
 +
 +<sxh shell>
 +# Build (Fat Jar 생성)
 +## build.sbt에
 +## addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.6")
 +## 추가 필요
 +### 생성된 jar 위치 : target/scala-[version]/*.jar
 +sbt assembly
 +
 +</sxh>
 +
 +
 +
 +=== Scala Binaries (선택 사항) ===
 +  * REPL(scala), 단독 Compile(scalac)을 이용하기 위해 필요
 +
 +<sxh shell>
 +# 실행
 +scala
 +
 +scala> 1
 +res0: Int = 1
 +
 +# 종료
 +scala> :quit
 +## 혹은
 +scala> :q
 +</sxh>
 +
 +
 +== IDE ==
 +- Intellij에 기본적으로 SBT 내장. Intellij만 설치하면 된다.
 +
 +=== REPL 실행 방법 ===
 +왼쪽 Project Tree에서 아무 Class에 오른쪽으로 누르고 "Run Scala Console".
 += Hello, world! =
 +
 +* 가변 인자 args를 입력 받아 "Hello, world!"를 출력하는 예제 작성
 +
 +== Script ==
 +
 +* Compile 하지 않고, Shell script 처럼 사용
 +
 +<sxh scala ; title:script.scala>
 +// class, main 함수를 선언하지 않고도 가변인자 args 사용 가능!
 +args.foreach(print)
 +println()
 +</sxh>
 +
 +<sxh bash>
 +> scala script.scala Hello , world !
 +Hello, world!
 +</sxh>
 +
 +
 +== Compile ==
 +
 +https://www.scala-lang.org/documentation/your-first-lines-of-scala.html
 +
 +
 +=== App trait 상속 O ===
 +
 +<sxh scala ; title:HelloWorld.scala>
 +object HelloWorld extends App {
 +  // class, main 함수를 선언하지 않고도 가변인자 args 사용 가능!
 +  args.foreach(print)
 +  println()
 +}
 +</sxh>
 +
 +<sxh shell>
 +> scalac HelloWorld.scala
 +> scala HelloWorld Hello , world !
 +</sxh>
 +
 +
 +=== App trait 상속 X ===
 +
 +<sxh scala ; title:HelloWorld.scala>
 +
 +object HelloWorld {
 +  def main(args: Array[String]): Unit = {
 +    args.foreach(print)
 +    println()
 +  }
 +}
 +</sxh>
 +
 +<sxh shell>
 +> scalac HelloWorld.scala
 +> scala HelloWorld Hello , world !
 +</sxh>
 +
 +
 +
 += 문법 =
 +
 +== Identifier ==
 +식별자 규칙 및 관례적 명명법 서술.
 +
 +  * 연산자 정의(Operator Defination) Method와 암시적 변환([[language:scala#Implicit Parameters and Conversions|Implicit Parameters and Conversions]])는 간결하고 유연한 표현을 정의할 수 있지만, 잘못 사용하면 가독성을 떨어뜨리는 결과를 낳으니 주의가 필요하다.
 +
 +
 +=== Alphanumeric Identifier ===
 +
 +# Java와 동일하게 영숫자 혹은 '_', '$'가 가능하나 '$' Scala Compiler가 내부적으로 생성하는 식별자에 사용하는 예약 문자이므로 충돌 가능성이 있기 때문에 사용해서는 안된다.
 +# 상수는 첫글자만 대문자로, Camel Case 표현을 한다. (Java 표현으로 MAX_VALUE라면, Scala는 MaxValue)
 +
 +=== Operator Identifier ===
 +
 +* Java와 가장 큰 차이점 중 하나. 이를 이용하면 Operator Overloading이 가능. Method에만 적용 가능.
 +* +, :, ?, ~, #, +, ++, ::(List에서 요소 덧붙임), :::(List에 다른 List 추가), <?>, :-> 등 사용 가능. 이 때 길이 제한이 없다.
 +
 +
 +* Overloading 예시
 +
 +<sxh scala>
 +def *** (v: Int) = math.pow(v, 3)
 +</sxh>
 +
 +==== 연산자 우선순위와 결합법칙 ====
 +
 +**:**(Cons; Construct)로 끝나는 연산자는 right-associative. 이외 나머지는 left-associative
 +
 +<sxh>
 +// left-associative
 +a * b * c
 += (a * b) * c
 += a.*(b).*(c)
 +
 +// right-associative
 +a ::: b ::: c
 += a ::: (b ::: c)
 += b.:::(c).:::(a)
 +</sxh>
 +
 +
 +=== Mixed Identifier ===
 +
 +Alphanumeric + Operator. 예를 들어 myvar_=. Scala Compiler가 Property를 지원하기 위해 내부적으로 생성.
 +
 +
 +=== Literal Identifier ===
 +
 +역따옴표(`)를 사용하며 Runtime에서 인식할 수 있는 문자열 생성. 이를 이용하면 예약어도 변수나 Method 이름으로 지정 가능.
 +
 +<sxh scala>
 +// 아래 코드는 Compile 및 실행에 문제가 없다.
 +
 +val `val` = 3;
 +def show(`var` : Int) = println(`var`);
 +
 +show(`val`)
 +</sxh>
 +
 +== 변수 ==
 +* val : Immutable
 +* var : mutable
 +
 +
 +
 +== Type ==
 +
 +=== Type Hierarchy ===
 +
 +{{https://docs.scala-lang.org/resources/images/tour/unified-types-diagram.svg}}
 +
 +|<100%>|
 +^  Any     |모든 type의 supertype. equals, hashCode, toString 메서드가 정의됨.  |
 +^  AnyVal  |value type을 표현, non-nullable. Double, Float, Long, Int, Short, Byte, Char, Unit, Boolean의 상위 타입.  |
 +^  AnyRef  |referenced type을 표현, java.lang.Object에 대응.  |
 +^  Nothing |모든 type의 subtype (i.e. bottom type)으로 value를 가지고 있지 않다.  |
 +^  Null    |모든 reference type의 subtype. keyword literal 'null'을 값으로 가지고 있다.  |
 +
 +
 +=== Literals ===
 +
 +==== Integer Literals ====
 +
 +* C/Java와 달리 8진(0으로 시작하는 정수형)을 지원하지 않음. 10진과 16진만 지원.
 +
 +==== String Literals ====
 +
 +===== Raw Sring =====
 +큰따옴표 3개(<nowiki>"""</nowiki>)를 이용하여 그 내부 문자열을 "날 것" 그대로 표현하는 문자열. (공백, 개행 전부 포함)
 +
 +<sxh scala>
 +object Test extends App {
 +  // Java에서의 일부 특수문자 표현. 가독성이 좋지 않음.
 +  val str1 = "Hello\n\"world\"";
 +  println(str1)
 +
 +  /**
 +    * Hello
 +    * "world"
 +    */
 +
 +
 +  // 공백, 개행 모두 그래도 출력
 +  val str2 =
 +    """Hello,
 +      "world"!
 +    """
 +  println(str2)
 +
 +  /**
 +    * Hello
 +    * "world"
 +    */
 +
 +
 +  // |를 넣으면 문자열의 시작을 알림. 이후 stripMargin을 하면 이전 공백 제거
 +  val str3 =
 +    """         |Hello,
 +      |"world!"
 +    """.stripMargin
 +  println(str3)
 +
 +  /**
 +    * Hello
 +    * "world"
 +    */
 +}
 +</sxh>
 +
 +===== String interpolation =====
 +문자열 내부에 표현식(expression)을 내장시키는 것. 이로써 문자열을 이어붙이지 않고 가독성이 좋아짐.
 +  * s : "$val" 혹은 "${expression}"과 함께 쓰여 mapping.
 +  * raw : 문자열 그대로 출력.
 +  * f : printf 형태의 형식 지정.
 +
 +
 +|<100%>|
 +^        ^  Description                                      Example  ^
 +|  s      "$val" 혹은 "${expression}"과 함께 쓰여 mapping.  |  <sxh scala>
 +val str = "9 * 9 = "
 +println(s"$str ${9 * 9}")
 +/**
 + * 9 * 9 =  81
 + */
 +</sxh>  |
 +|  raw    문자열 그대로 출력.  |  <sxh scala>
 +println(raw"A\nB\\C\tD\bE")
 +
 +/**
 + * A\nB\\C\tD\bE
 + */
 +</sxh>  |
 +|  f  |  printf 형태의 형식 지정. \\ ${expression}%[format]  |  <sxh scala>
 +println(f"${math.Pi}%.7f")
 +println(f"${3 * 3}%03d")
 +
 +/**
 + * 3.1415927
 + * 009
 + */
 +</sxh>  |
 +
 +
 +==== Symbol literals ====
 +
 +* 작은 따옴표(') + (알파벳 + 숫자)
 +* 단순 식별자가 필요할 때 사용. Java의 enum이나 [[language:clojure#keyword|Clojure의 Keyword]]와 비슷.
 +  * Scala의 Strong Type 언어이기 때문에 Weak Type 형태로 사용할 때 사용.
 +  * [[language:clojure#keyword|Clojure의 Keyword]]처럼 자기 자신을 반환
 +  * .name 필드로 문자열 변환 가능.
 +
 +
 +=== Array ===
 +
 +* 모든 것을 Method가 있는 객체로 다루기 때문에 Parameter에 해당하는 호출은 **()**을 사용한다.
 +
 +<sxh scala>
 +val fruits = new Array[String](2)
 +// val fruits: String = new Array[String](2)
 +
 +fruits(0) = "apple"
 +// fruites.update(0, "apple")
 +
 +fruits(1) = "banana"
 +// fruites.update(1, "banana")
 +</sxh>
 +
 +<sxh scala>
 +val fruits = Array("apple", "banana")
 +// val fruits = Array.apply("apple", "banana")
 +</sxh>
 +
 +
 +== Operator ==
 +
 +=== Operator = Method! ===
 +
 +Scala에서 모든 연산자는 Method!! 실질적으로 Scala는 Method를 호출한다.
 +
 +<sxh scala>
 +/** Binary Operator **/
 +
 +val sum = 1 + 2
 +// 1.+(2)
 +
 +"Hello, world!" indexOf 'o'
 +// "Hello, world!".indexOf('o')
 +
 +"Hello, world!" indexOf ('o', 5)
 +// "Hello, world!".indexOf('o', 5)
 +
 +
 +
 +/** Unary Operator **/
 +
 +/*** 전위 표기법 ***/
 +// +,0,!,~ 4가지.
 +
 +val a = -2.0
 +// (2.0).unary_-
 +
 +
 +/*** 후위 표기법 ***/
 +// 인자를 취하지 않는 Method를 '.'이나 괄호 없이 호출
 +
 +val str = "Hello, world!" toLowercase
 +// val str = "Hello, world!".toLowercase()
 +</sxh>
 +
 +
 +=== == & eq ===
 +Java와 동일하다. \\ <nowiki>==</nowiki>, !=은 원시 타입에서 값(value)이 같은지 비교, eq/ne는 JVM Heap에서 참조(reference) 비교
 +
 +<sxh scala>
 +scala> List(1, 2, 3) == List(1, 2, 3)
 +res8: Boolean = true
 +
 +scala> List(1, 2, 3) eq List(1, 2, 3)
 +res9: Boolean = false
 +
 +scala> List(1, 2, 3) ne List(1, 2, 3)
 +res10: Boolean = true
 +
 +// 주의!
 +scala> ("He" + "llo") == "Hello"
 +res11: Boolean = true
 +</sxh>
 +
 +
 +=== Wrapper ===
 +
 +<sxh scala>
 +scala> 111 max 222
 +res0: Int = 222
 +
 +scala> 111 min 222
 +res1: Int = 111
 +
 +scala> -math.Pi abs
 +res2: Double = 3.141592653589793
 +
 +scala> -math.Pi round
 +res3: Long = -3
 +
 +scala> (1.0 / 0) isInfinity
 +res4: Boolean = true
 +
 +scala> 1 to 100
 +res5: scala.collection.immutable.Range.Inclusive = Range 1 to 100
 +
 +scala> "hello" capitalize
 +res6: String = Hello
 +
 +scala> "Hello, World" drop 7
 +res7: String = World
 +</sxh>
 +
 +
 +== 내장 제어 구문 ==
 +
 +**전체적으로 재귀나 내장함수(reduce, map, filter, reduce)로 대체가 가능하기 때문에 사용을 자제하자. 제어 구문은 함수형 표현에 적합하지 않다.**
 +
 +  * 키워드로써 break, continue는 존재하지 않는다. (함수형 언어에 어울리지 않으므로.)
 +    * 굳이 break를 쓰고 싶다면 scala.util.control.Breaks 에 있는 것을 사용할 수 있다...만...
 +  * 제어 구문들도 함수이므로 var/val에 할당할 수 있다. (이 때, 기본적으로 바로 계산한다. lazy loading이 필요하면 lazy를 맨 앞에 붙이자.)
 +  * while은 대체로 중간 결과를 저장하는 변수 var가 필요하기 때문에 사용을 자제하자.
 +
 +
 +=== for ===
 +
 +foreach 문만 존재한다. 그러나 대체로 반복하는 경우 내장 함수(foreach, reduce, map, filter 등)이 존재하기 때문에 특별한 경우가 아니면 큰 필요성이 없다.
 +
 +<sxh scala>
 +/* Collections */
 +
 +//10 ~ 100
 +for (i <- 10 to 100)
 +    println(i)
 +    
 +//10 ~ 99 (최대값 제외)
 +for (i <- 3 until 10)
 +    println(i)
 +   
 +
 +/* Iterator Guard / Filter. if 표현식이 true일 때만 반복 */
 +for (file <- new java.io.File(".").listFiles()
 +        if file.isFile
 +        if file.getName.endsWith(".scala"))
 +    println(file)
 +
 +/* 중첩 및 임시 변수 Binding */
 +// 구구단 출력
 +for {x <- (1 to 9)
 +     y <- (1 to 9)
 +     result = x * y}
 +    println(f"$x * $y = $result%02d")
 +</sxh>
 +
 +
 +==== for-yield ====
 +
 +<EXPRESSION>의 값을 Collection로 반환. (//map// 과 같은 효과)
 +
 +<code scala>
 +for (<IDENTIFIER> <- <ITERATOR>) yield <EXPRESSION>
 +</code>
 +
 +<sxh scala>
 +  // 인자가 없기 때문에 ()를 생략한 형태.
 +  def timesTable = for {
 +               x <- (1 to 9)
 +               y <- (1 to 9)
 +               result = x * y}
 +    yield f"$x%2d * $y%2d = $result%2d"
 +
 +  println(timesTable)
 +  
 +  // Vector( 1 *  1 =  1,  1 *  2 =  2, ... ,  9 *  8 = 72,  9 *  9 = 81)
 +</sxh>
 +
 +=== try-catch ===
 +
 +  * try, finally는 단 하나의 식만 포함한다면 중괄호 생략 가능.
 +
 +<sxh scala>
 +  try {
 +    val f = new FileReader(".")
 +  } catch {
 +    case ex: FileNotFoundException => ex.printStackTrace()
 +    case ex: IOException => ex.printStackTrace()
 +  } finally println("done")
 +</sxh>
 +
 +
 +  * 다른 제어 구조, 함수와 마찬가지로 결과로 값을 반환한다. 이 때, finally 절의 값은 버려진다.
 +  * finally에는 값을 반환하지 말고 Side-effect(결과 완료 알림, 파일 저장등)만 이용하자.
 +
 +<sxh scala>
 +  // 결과 : 2
 +  // Java 형식으로 finally 절 안에 return문을 명시적으로 사용하면 try/cath의 결과를 덮어쓰게 된다.
 +  // 잘 생각해보면 Java에서 finally는 어떤 결과로든 항상 실행되는 구문이기 때문.
 +  def test1(): Int = try return 1 finally return 2
 +
 +  // 결과 : 1
 +  // Scala 형식으로 작성하면 의도된대로 가장 처음에 만나는 값을 반환한다.
 +  def test2(): Int = try 1 finally 2
 +  
 +  // 결론적으로 finally에는 값을 사용하지 말자.
 +</sxh>
 +
 +
 +=== match ===
 +case의 대안. Java와 달리 타입에 상관없이 어떤 상수값이라도 사용 가능. 이 때, match 앞에 오는 비교대상의 타입을 기본적으로 다른다. (그렇게 되면 case로 비교하는 값들도 그 타입을 따른다.) '_'은 default에 대응하며 "완전히 알려지지 않은 값"을 의미.
 +
 +<sxh scala>
 +object Client extends App {
 +  def matchTest(x: Any): Any = x match {
 +    case 1 => "one"
 +    case "two" => 2
 +    case y: Int => "scala.Int"
 +    case _ => 'unknown
 +  }
 +
 +  println(matchTest(3)) // scala.Int
 +}
 +</sxh>
 +
 +
 +== Functions ==
 +
 +* 재귀 함수인 경우 반드시 Return Type 명시, 그외에는 생략 가능.
 +  * 함수 길이 긴 경우 사람이 읽기 쉽게 Return Type을 명시하는 게 좋음.
 +  * "return" 구문은 선택. 생략 가능.
 +* 본론 문장이 하나면 괄호 생략 가능
 +* Retury Type의 "Unit"은 Java의 void. 즉, Side-effect를 위해서만 실행하는 함수.
 +* parameter는 val이므로 값을 재할당할 수 없다. (예를 들어 Call-by-reference를 이용하려는 경우)
 +* 함수 안에 함수를 정의할 수 있다.
 +* 인자가 없는 메소드는 소괄호를 생략할 수 있다. 그러나 관례로서 Side-Effect가 있거나 함수 본래 작업 이외의 부가적인 작업이 존재할 경우 소괄호를 붙인다.
 +
 +<sxh scala>
 +def sum(x: Int, y: Int): Int = {
 +  x + y
 +}
 +
 +// 위와 같은 표현
 +def sum(x: Int, y: Int) = x + y
 +</sxh>
 +
 +
 +<sxh scala>
 +// Unit 함수. Return 값이 없음을 의미.
 +// Type 추론이 되므로 Return Type에 Unit 생략 가능.
 +def greeting(): Unit = println("Hello, world!")
 +</sxh>
 +
 +
 +=== Local functions ===
 +함수의 중첩이 가능.
 +
 +<sxh scala ; title:지역함수>
 +def test1(x: Int) = {
 +  def test1(y: Int) = x * y
 + test1(10)
 +}
 +
 +println(test1(5)) // 50
 +</sxh>
 +
 +
 +=== Placeholder syntax ===
 +위치 표시자.
 +
 +<sxh scala>
 +  /* 고차함수를 더 간략하게 작성하는 방법 */
 +
 +  val values = 1 to 10
 +
 +  values.filter((x: Int) => x > 5)
 +
 +  // filter의 인자는 Int를 받도록 명시되어 있으므로 생략 가능
 +  values.filter(x => x > 5)
 +
 +  // 위치표시자(_)를 이용하면 더 간략한 표현 가능.
 +  values.filter(_ > 5)
 +
 +  // 아래와 같은 응용도 된다.
 +  values.foreach(println _) // values.foreach(x => println(x))
 +  // 그런데 foreach의 인자가 명확한 위치가 나타나기 때문에 생략 가능
 +  values.foreach(println)
 +  
 +  val func1 = (_:Int) + (_:Int)
 +  println(func1(1, 1)) // 2
 +</sxh>
 +
 +=== Partitially applied function ===
 +
 +<sxh scala>
 +  def sum(a: Int, b: Int, c: Int) = a + b + c
 +
 +  // sum 함수를 대입. 이 때, _의 의미는 sum의 모든 인자를 의미.
 +  val a = sum _
 +  println(a(1, 2, 3)) // = a.apply(1, 2, 3)
 +
 +  // 아래와 같이 부분적인 인자 적용도 가능.
 +  val b = sum(1, _:Int, 3)
 +  println(b(2)) // = b.apply(2)
 +</sxh>
 +
 +=== Closure ===
 +
 +<sxh scala>
 +  /* Java의 경우 final(immuitable)로 선언해야만 접근 가능한 것에 비해 var(mutable)도 사용 가능. */
 +  /* Javascript와 동일. */
 +
 +  /* 변수 바인딩 */
 +  var sum = 0
 +  (1 to 10).foreach(sum += _) ;; 바깥 범위(scope)에 있는 sum의 값에 누적.
 +
 +  println(sum) // 55
 +  
 +
 +  /* 함수 바인딩 */
 +  def scope1(x: Int) = {
 +    /*def scope2(y: Int) = x + y
 +    scope2 _*/
 +
 +    // 위와 같은 표현
 +    x + (_: Int)
 +  }
 +
 +  // 각 scope1에 바인딩된 바깥쪽 x를 다르게 설정
 +  val closure1 = scope1(1)
 +  val closure2 = scope1(2)
 +
 +  println(closure1(10)) // 11
 +  println(closure2(10)) // 12
 +</sxh>
 +
 +
 +=== Paramter & Argument ===
 +
 +<sxh scala>
 +  /* repeated parameter. 가변 인자(variable paramter)와 동일 개념 */
 +  def test1 (args: String*) = args.foreach(println)
 +  test1("Hello", "world", "!")
 +  test1(Array("Hello", "world", "!"): _*)
 +
 +  /* named argument. 순서 상관없이 인자의 이름으로 전달 */
 +  def test2 (v1: Int, v2: Int) = v1 + v2
 +  println(test2(v2 = 1, v1 = 9))
 +
 +  /* default argument. 인자의 값이 없을 경우 default로 값을 미리 지정 */
 +  def test3 (v1: Int = 1, v2: Int) = v1 + v2
 +  println(test3(v2 = 1))
 +</sxh>
 +
 +
 +=== Currying ===
 +
 +함수를 1급 객체로 취급하기 때문에 가능.
 +다중 인수를 갖는 함수를 단일 인수를 갖는 함수들의 함수열로 바꾸는 것.
 +
 +<sxh scala>
 +def curriedSum(x: Int)(y: Int) = x + y
 +  //def curriedSum(x: Int) = (y: Int) => x + y
 +
 +  println(curriedSum(1)(2))
 +</sxh>
 +
 +=== By-name parameters ===
 +
 +값, 함수 모두 "값"으로 평가하면서 인자를 넘기고 싶은 경우 사용.
 +
 +이 역시 함수를 1급 객체로 취급하기 때문에 가능.
 +paramter의 Lazy evalution. 성능상의 이점을 가진다.
 +
 +<sxh scala>
 +  def test1(b: Boolean) = b
 +  test1(2 > 1)
 +
 +  // by-name parameter. lazy evaluation.
 +  def test2_1(b: () => Boolean) = b
 +  test2_1(() => 2 > 1)
 +
 +  // 개선
 +  def test2_2(b: => Boolean) = b
 +  test2_2(2 > 1)
 +</sxh>
 +
 +
 +== Collections ==
 +
 +* https://docs.scala-lang.org/overviews/collections-2.13/overview.html
 +* https://docs.scala-lang.org/overviews/collections-2.13/performance-characteristics.html
 +
 +기본적으로 불변(Immutable) 상태를 가진다.
 +
 +
 +
 +=== Seq ===
 +
 +선형 자료 구조.
 +
 +* 아래 두 관점으로 적합한 자료구조를 선택.
 +  * "Indexing"이 필요한가?
 +  * "mutable state"가 필요한가?
 +
 +==== Immutable ====
 +
 +{{ :language:scala.collection.immutable.seq.png |}}
 +
 +* immutable.LazyList는 immutable.Stream을 대체. immutable.Stream 는 2.13부터 Deprecated.
 +* immutable.ArraySeq 는 2.13부터 추가되었는데, 왠만하면 성능면에서 사실상 상수 시간(Constant time)을 가지는 Vector를 사용하는게 이득.
 +
 +
 +===== List =====
 +
 +Singly Linked List. 앞부분에 추가/삭제 유리. 임의 접근은 불리.
 +
 +* ::: : List를 이어 붙여 새로운 List 반환
 +* ::  : Cons(콘즈) 라고 부르며, 새 원소를 기존 List 앞에 삽입한 새로운 List 반환 <sxh scala ; gutter:false>
 +[새 원소] :: [기존 List]
 +</sxh>
 +
 +* mutable 상태는 collecdtion.mutable.Buffer
 +
 +<sxh scala ; title:초기화>
 +val test1 = List(1, 2, 3)
 +val test1 = 1 :: 2 :: 3 :: Nil
 +</sxh>
 +
 +<sxh scala ; title:응용>
 +val test1 = List(1, 2) ::: List(3, 4) ::: List(5, 6)
 +// List(1, 2, 3, 4, 5, 6)
 +
 +val test2 = 1 :: List(2, 3)
 +// List(1, 2, 3)
 +
 +val test3 = List(1, 2) :: List(2, 3)
 +// List(List(1, 2), 2, 3)
 +</sxh>
 +
 +
 +===== List vs. Vector =====
 +
 +[[https://docs.scala-lang.org/overviews/collections-2.13/performance-characteristics.html|전체 Collections 성능 비교]]
 +
 +^        ^  head ^ tail ^ apply ^ update ^ prepend ^ append ^
 +| List        C            |    C    |  L     |
 +| Vector |   eC  |  eC  |   eC  |   eC      eC    eC    |
 +
 +
 +* C (Constant) : 연산에 상수 시간이 걸린다. (빠름)
 +* eC (Effectively Constant) : 연산에 사실상 상수 시간이 걸리는데, 이는 Vector의 최대 길이나 Hash key 분포와 같은 일부 가정에 따라 달라질 수 있다.
 +* L (Linear) : 연산에 선형 시간이 걸린다. 즉, Collections 크기에 비례하여 시간이 걸린다.
 +
 +
 +**결론적으로 head가 임의 위치 접근이 필요한 경우 Vector 선택.**
 +
 +
 +==== Mutable ====
 +
 +{{ :language:scala.collection.mutable.seq.png |}}
 +
 +
 +=== Set ===
 +
 +* 같은 이름의 mutable, immutable 패키지가 있으니 주의.
 +
 +<sxh scala>
 +import scala.collection.mutable
 +
 +val set1 = Set("a", "b")
 +
 +// 원소 추가
 +// Collection이 val인 경우, mutable package의 import 필요!
 +/// set1.+=("b") 와 같은 표현
 +set1 += "b"
 +</sxh>
 +
 +
 +=== Map ===
 +
 +<sxh scala>
 +// immutable이기 때문에 원소를 추가할 수 없음. 이어붙이는 연산을 해야 함.
 +
 +val map1 = Map("A" -> 1, "B" -> 2, "C" -> 3)
 +val map2 = Map("D" -> 4, "E" -> 5, "F" -> 6)
 +val map3 = map1 ++ map2
 +
 +println(map3)
 +
 +// 기본 구현체는 immutable.HashMap
 +// HashMap(E -> 5, F -> 6, A -> 1, B -> 2, C -> 3, D -> 4)
 +</sxh>
 +
 +<sxh scala ; highlight: [1]>
 +import scala.collection.mutable
 +
 +val map1 = Map[String, Int]()
 +
 +map1 += ("One" -> 1)
 +map1 += ("Two" -> 2)
 +
 +map1("One")
 +</sxh>
 +
 +<sxh scala>
 +val map1 = Map("One" -> 1, "Two", 2)
 +map1("One")
 +</sxh>
 +
 +
 +==== TrieMap vs. ConcurrentHashMap ====
 +
 +* scala.collection.concurrent.TrieMap
 +* java.util.concurrent.ConcurrentHashMap
 +
 +둘 다 동시성을 보장하는 Thread-Safe Map.
 +
 +
 +* Reference
 +  * https://stackoverflow.com/questions/29499381/what-is-a-triemap-and-what-is-its-advantages-disadvantages-compared-to-a-hashmap
 +  * https://www.researchgate.net/publication/221643801_Concurrent_Tries_with_Efficient_Non-Blocking_Snapshots
 +
 +
 +
 +=== Tuple ===
 +
 +엄격히 말하면 Collections에 포함되지 않음.
 +
 +* Tuple22 까지, 즉 22개의 Argument까지 지원.
 +
 +<sxh scala>
 +val tp = (123, "Hello", 1.23)
 +// val tp = Tuple3[Int, String, Double](123, "Hello", 1.23)
 +
 +print(tp._1)
 +print(tp._2)
 +print(tp._3)
 +</sxh>
 +
 +
 +== Class & Object ==
 +
 +=== 기초적으로 알아 두어야 할 사항 ===
 +
 +* Procedure : Side-effect만을 위해서 실행되는 Method (≒ Return Type이 없는, Unit인 Method)
 +* static Member가 없다. 대신, Singleton Object를 제공한다.
 +* Java는 Field, Method, Type, Package의 4가지 Namespace를 갖지만 Scala는 Field, Method를 2가지 Namespace 갖는다.
 +  * Field가 Parameter없는 Method를 Override할 수 있다. (서로 동일시 한다.)
 +    * 이 같은 성질로 같은 Class에 같은 이름의 Field와 Method를 정의할 수 없다.
 +  * Package도 Field 및 Method처럼 Namespace를 공유하는 이유는 Singleton Object에서 Field와 Method를 import하기 위해서다.
 +
 +
 +=== Access Modifier ===
 +
 +|<100%>|
 +^  Modifier          ^  Class  ^  Companion  ^  Subclass  ^  Package  ^
 +|  public (default)  |  Y      |  Y          |  Y          Y        |
 +|  protected          Y      |  Y          |  Y          N        |
 +|  private            Y      |  Y          |  N          N        |
 +
 +* Y : Field/Method 접근 가능
 +* N : Field/Method 접근 불가
 +
 +
 +=== Class ===
 +
 +* Java와 달리 Class 이름과 Class가 작성된 파일 이름이 같을 필요가 없으나 권장.
 +
 +<sxh scala ; title:기본>
 +class Person {
 +  // member 변수와 Getter/Setter 서술
 +}
 +</sxh>
 +
 +==== Constructor ====
 +
 +===== Primary Constructor =====
 +
 +  * 주 생성자만이 Super Class의 생성자 호출 가능!
 +
 +<sxh scala ; title:Constructor>
 +// 아래와 같이 class를 선언하면 member 변수들은 private val
 +// 이후 값을 변경할 수 없음.
 +class Person (name: String, age: Int) {
 +  def getName: String = name
 +  def getAge : Int = age
 +}
 +</sxh>
 +
 +<sxh scala ; title:Constructor+Getter>
 +// 아래와 같이 class를 선언하면 member 변수들은 public val
 +/// val을 선언함으로써 Getter 생성. ".name", ".age"로 접근.
 +// 바로 멤버 변수에 접근할 수 있고, 이후 값을 변경할 수 없음.
 +
 +// Class 뒤의 인자들을 "Class Parameter"라고 지칭한다.
 +// Scala Compiler는 내부적으로 Class Parameter를 기반으로 Primary Constructor(주 생성자)를 생성한다.
 +/// 기본적으로 Scala의 Constructor는 Class Parameter를 생성하지 않는다. val을 붙이면 생성한다.
 +class Person (val name: String, val age: Int)
 +</sxh>
 +
 +<sxh scala ; title:Constructor_특성>
 +// Scala Compiler는
 +// Class 내부에 있으면서
 +// Field나 Method 정의에 들어 있지 않은 코드를
 +// Primary Constructor에 삽입한다.
 +
 +/// 객체 생성과 동시에 println 메시지 출력.
 +class Person (name: String, age: Int) {
 +  println("Created!" + name + " / " + age)
 +}
 +</sxh>
 +
 +
 +===== Auxiliary Constructor =====
 +
 +  * 모든 보조 생성자는 __<fc red>**반.드.시.**</fc> 같은 클래스에 속한 다른 생성자를 호출__하는 코드로 시작해야 한다. 결국 주 생성자를 호출하게 만드는 효과가 있다.
 +  * 주 생성자만이 Super Class의 생성자 호출 가능!
 +
 +<sxh scala ; highlight:[2]>
 +class Person (val name: String, val age: Int) {
 +  def this(age: Int) = this("meteor", age);
 +
 +  override def toString: String = name + " / " + age
 +}
 +</sxh>
 +
 +
 +==== Checking Preconditions ====
 +
 +  * 유효성 검사 때문에 생성되는 try ~ catch, if ~ else 에서 벗어날 수 있다!
 +  * 조건을 만족시키지 못하면 IllegalArgumentException 발생. 메시지가 없으면 "requirement failed"
 +
 +<sxh scala ; highlight:[2,3]>
 +class Person (name: String, age: Int) {
 +  require(name != null, "name is not nullable.")
 +  require(age > 0)
 +  override def toString: String = name + " / " + age
 +}
 +</sxh>
 +
 +
 +==== Operator Defination ====
 +Operator Overloading.
 +
 +<sxh scala ; highlight:[2]>
 +class Person (val name: String, val age: Int) {
 +  def +(p: Person) = this.toString + "\n" + p.toString
 +
 +  override def toString: String = name + " / " + age
 +}
 +</sxh>
 +
 +<sxh scala>
 +object Client extends App {
 +  val p1 = new Person("hyuk", 30)
 +  val p2 = new Person("meteor", 26)
 +  println(p1 + p2)
 +}
 +</sxh>
 +
 +
 +==== Method Overloading =====
 +
 +Java와 동일하나 Scala는 Type을 유추하기 때문에 적합한 Method의 Parameter Type 일치 결과가 없다면 'ambiguous reference' 오류를 출력. 그리고 Overloading보다 [[https://docs.scala-lang.org/tour/default-parameter-values.html|DEFAULT PARAMETER VALUES]]를 이용을 권장. Method 수를 줄일 수 있다.
 +
 +
 +==== 'apply' Method ====
 +
 +'Default Method' 또는 'Injector Method'라고도 불린다. 이는 __Method 이름없이 **괄호**__를 사용하여 호출하는 기능을 갖고 있다. 
 +
 +<sxh scala>
 +val l = List(1, 2, 3)
 +
 +// 같은 의미
 +println(s"${l.apply(1)} ${l(1)}")
 +</sxh>
 +
 +위 예제의 'List.apply(index)' 처럼 상식적으로 이해할 수 있는, 자연스러운 연산에 적용되어야 한다.
 +
 +
 +=== object ===
 +
 +* class 대신 **object**로 시작. Java의 static class에 대한 대안.
 +  * Instance를 생성할 수 없다. 그러므로 당연히 Constructor도 선언 불가.
 +* "Singleton"으로 작동하므로 첫 접근이 되지 않는 한 인스턴스가 만들어지지 않는다.
 +* 대개 특정 객체에 상관없는 Side-effect Method들(대개 Service 목적의 Util 성질을 가진 공통 사용 Method)을 담아두고 사용하는 Method 사용. 
 +
 +<sxh scala ; title:Person.scala>
 +// Companion class
 +class Person (val name: String, val age: Int)
 +
 +// Companion object
 +object Person {
 +  def print(person: Person) :Unit = println(person.name + " / " + person.age)
 +}
 +</sxh>
 +
 +<sxh scala>
 +object Client extends App {
 +  val person: Person = new Person("Amy", 11)
 +  Person.print(person)
 +}
 +
 +/* Amy / 11 */
 +</sxh>
 +
 +
 +==== Companion object ====
 +
 +어떤 class **이름**이 같은 object. 그 피대상인 class는 "Companion class"라고 한다. Companion object와  [[language:scala#'apply' Method|'apply' Method]]를 이용하여 Companion class의 [[design_pattern:factory_method_pattern|Factory Pattern]] 을 적용할 때 사용.
 +이 때, object의 field들이 "private"으로 선언되어 있더라도 접근 가능.
 +
 +Companion class와 Companion object는 **<fc red>__반드시__</fc> 같은 소스 파일**에 있어야 한다!
 +
 +<sxh scala>
 +object DBConnection {
 +  private val url = "jdbc://localhost"
 +  private val user = "franken"
 +  private val password = "berry"
 +
 +  def apply() = new DBConnection()
 +}
 +
 +class DBConnection {
 +  private val props = Map(
 +    "url" -> DBConnection.url,
 +    "user" -> DBConnection.user,
 +    "password" -> DBConnection.password
 +  )
 +}
 +
 +// val connection = DBConnection()
 +</sxh>
 +
 +
 +=== case class ===
 +
 +* 자동으로 유용한 Method들 생성. (Kotlin의 'data class'와 동일한 개념)
 +  * Data를 관리하는데 유용. 즉, DTO로 유용.
 +* 자동으로 [[language:scala#companion object|Companion Object]] 생성. "new" 필요하지 않음.
 +
 +* 기본적으로 생성자의 Parameter들은 'val'.
 +
 +* Compile하면 '**[소스이름].class(class)**', '**[소스이름]$.class(object)**'이 생성된다.
 +
 +
 +* 자동으로 생성되는 Methods
 +
 +^  이름    ^  위치  ^  설명  ^
 +| apply    | object |case class를 Instance로 만드는 Factory method  |
 +| unapply  | object |Instance의 field들을 Tuple로 추출하여 **[[language:scala#match|Pattern Matching]]**에 case class instance를 사용할 수 있도록 함  |
 +| copy     | class  | **Deep copy!** |
 +| equals   | class  | Reference가 아닌 구조적 동등성(모든 필드가 일치)으로 비교. '=='로도 호출 할 수 있다. |
 +| hashCode | class  | Instance의 field들의 Hash code 반환. hash 기반 collections에서 동등성에 이용.  |
 +| toString | class  | Field들을 String으로 전환 |
 +
 +
 +<sxh scala>
 +object Practice extends App {
 +  val person = Person("Luke", 33)
 +  val copy = person.copy()
 +  // Equals?
 +  println(person == copy)
 +
 +  person.name = "James"
 +  person.age = 11
 +  // Deep copy?
 +  println(copy)
 +
 + // Pattern Matching
 +  val p = person match {
 +    case Person(_, 11) => "Original!"
 +    case Person(_, 33) => "Copy!"
 +  }
 +  println(p)
 +}
 +
 +case class Person(var name: String, var age: Int)
 +</sxh>
 +
 +* Reference
 +  * https://docs.scala-lang.org/overviews/scala-book/case-classes.html
 +  * https://www.oreilly.com/library/view/scala-cookbook/9781449340292/ch04s15.html
 +
 +
 +
 +=== Composition and Inheritance ====
 +
 +==== Override ====
 +Java의 경우 1.5 기준으로 @Override를 명시할 수 있으나 필수사항이 아닌데 반면, Scala는 강제한다. 이로 인해 "우연한 Override"로 "fragile base class"가 생성되는 문제를 줄여준다.
 +
 +<sxh scala>
 +class Person (val name: String, val age: Int) {
 +  override def toString: String = name + " / " + age
 +}
 +</sxh>
 +
 +
 +==== Abstract Class ====
 +Java와 비교하여
 +
 +* method에 abstract를 붙이면 안된다.
 +  * abstract는 class 혹은 abstract class를 mix-in한 trait의 method만 붙일 수 있다.
 +  * 본체가 없으면 abstract method로 인식한다. (이는 trait도 동일)
 +* 본체가 있는 concrete method를 구현할 수 있다.
 +* **Field가 파라미터 없는 메소드를 Override 할 수 있다.**
 +
 +<sxh scala ; highlight:[2, 10]>
 +abstract class Abstract {
 +  /* abstract가 필요하지 않다. */
 +  def values: Array[Int]
 +  def sum = values.sum
 +}
 +
 +class Detail(conts: Array[Int]) extends Abstract {
 +  /* 아래 동작은 같다. */
 +  //override def values: Array[Integer] = conts
 +  val values: Array[Int] = conts
 +}
 +</sxh>
 +
 +==== Parameter field 정의 ====
 +같은 이름의 생성자 Paramter와 Field를 동시 정의하는 단축 표기. 특정 Field에 할당하는 이름 중복을 피하기 위한 Boilerplace 제거.
 +
 +<sxh scala ; highlight:[12]>
 +abstract class Abstract {
 +  def values: Array[Int]
 +}
 +
 +// 아래와 같이 같은 목적이지만 중복된 이름을 피하기 위해 불필요한 부분이 작성된다.
 +// 할당과 동시에 바로 getter 생성
 +class Detail(vals: Array[Int]) extends Abstract {
 +  override def values: Array[Int] = vals
 +}
 +
 +// 이를 아래와 같이 작성할 수 있음.
 +class Detail2(var values: Array[Int]) extends Abstract
 +</sxh>
 +
 +==== Super class의 Constuctor 호출 ====
 +Java의 경우 상속을 받으면 반드시 부모 Class의 생성자와 같은 형태의 생성자를 호출하고 super()를 하는 과정을 아래와 같이 간략화할 수 있다.
 +
 +<sxh scala ; highlight:[14]>
 +abstract class Abstract {
 +  def values: Array[Int]
 +  def sum = values.sum
 +}
 +
 +class Detail(val values: Array[Int]) extends Abstract
 +/*
 +class Detail(vals: Array[Int]) extends Abstract {
 +  override def values: Array[Int] = vals
 +}
 +*/
 +
 +/* Super class(Detail)의 생성자 호출. */
 +class MoreDetail(x: Int) extends Detail(Array(x)) {
 +}
 +</sxh>
 +
 +
 += Trait =
 +Java의 Interface의 역할을 하는 한 단위.
 +
 +* 두 가지를 제외하고 Class가 할 수 있는 일을 다 할 수 있다. (Field 정의, 본체가 있는 concrete method 구현등등)
 +  * 할 수 없는 두 가지 : 생성자 Parameter 구현, super를 이용한 Method 정적 바인딩
 +* Mix-in 대상은 Trait뿐만이 아니라 Class, Abstract Class 모두 다 된다.
 +* 첫 번째 상속/구성할 대상이 Class든 Trait든 무조건 extends를 사용한다. 단, Class와 Trait를 혼합사용(Mix-in)할 경우 Class를 먼저 기술한다. 이후 Trait를 with를 이용하여 Mix-in 한다.
 +  * 즉, Java의 Interface와 달리 Class도 상속이 가능하다! 이는 해당 부모 Class의 Method를 Override가 필요한 상황에서 사용한다.
 +
 +<sxh scala>
 +object Client extends App {
 +  val x:D = new D
 +  x.func
 +  // "안녕, 세상아!"
 +}
 +
 +trait T1 {
 +  def func(): Unit
 +}
 +
 +trait T2 {
 +  private val greeting = "Hello, world!"
 +  def func() = println(greeting)
 +}
 +
 +class C1 {
 +}
 +
 +class D extends C1 with T1 with T2 {
 +  override def func(): Unit = println("안녕, 세상아!")
 +}
 +</sxh>
 +
 +
 +== Stackable Modifications ==
 +
 +<sxh scala ; highlight: [2, 21-22]>
 +object Client extends App {
 +  val queue = new BasicIntQueue with Doubling
 +  queue.put(10)
 +
 +  println(queue.get())
 +  // 20
 +}
 +
 +abstract class IntQueue {
 +  def get(): Int
 +  def put(x: Int)
 +}
 +
 +class BasicIntQueue extends IntQueue {
 +  private val buf = new ArrayBuffer[Int]
 +  override def get(): Int = buf.remove(0)
 +  override def put(x: Int) = buf += x
 +}
 +
 +trait Doubling extends IntQueue {
 +  // trait에서 super로 접근할 때는 override abstract가 필요.
 +  override abstract def put(x: Int): Unit = super.put(x * 2)
 +}
 +</sxh>
 +
 +
 +=== 여러 Trait를 mix-in 했을 때 우선 순위 ===
 +
 +가장 오른쪽에 있는 Trait의 효과부터 적용된다.
 +
 +<sxh scala>
 +trait Base {
 +  override def toString: String = "Base"
 +}
 +
 +
 +trait A extends Base {
 +  override def toString: String = s"A->${super.toString}"
 +}
 +
 +trait B extends Base {
 +  override def toString: String = s"B->${super.toString}"
 +}
 +
 +trait C extends Base {
 +  override def toString: String = s"C->${super.toString}"
 +}
 +
 +
 +class MixIn extends A with B with C {
 +  override def toString: String = s"MixIn->${super.toString}"
 +}
 +
 +// println(new MixIn())
 +/// MixIn->C->B->A->Base
 +</sxh>
 +
 +
 += Type parameterization =
 +
 +== Bounded Type ==
 +
 +Type Parameter를 특정 Class나 거의 Sub type 또는 Base type으로 **<fc red>제한</fc>**하는 방법.
 +
 +=== Upper bound ===
 +
 +Type을 Base type 또는 Sub type 중 하나로 제한한다.
 +
 +https://docs.scala-lang.org/tour/upper-type-bounds.html
 +
 +<sxh>
 +<identifier> <: <upper bound type>
 +</sxh>
 +
 +<sxh scala ; highlight:[8]>
 +object Practice extends App {
 +  def check[A <: Parent](u: A): Unit = {
 +    println(s"${u.name}")
 +  }
 +
 +  // Runtime Error
 +  // inferred type arguments [GrandParent] do not conform to method check's type parameter bounds [A <: Parent]
 +  check(new GrandParent("Jake"))
 +
 +  check(new Parent("Fred"))
 +  check(new Child("Mike"))
 +}
 +
 +class GrandParent(val name: String)
 +class Parent(name: String) extends GrandParent(name)
 +class Child(name: String) extends Parent(name)
 +</sxh>
 +
 +=== Lower bound ===
 +
 +Type을 해당 Type으로 제한하거나 또는 해당 Type이 확장되는 Base type 중 하나로 제한한다.
 +
 +<sxh>
 +<identifier> >: <lower bound type>
 +</sxh>
 +
 +
 +== Type variance ==
 +
 + Type parameter를 **<fc red>덜 제한적</fc>**으로 만드는 방법.
 +타입 가변성(Type variance)은 Type parameter가 Base type이나 Sub type을 충족하도록 적응하는 방법을 지정한다.
 +
 +|<100%>|
 +^   Scala  ^  Java              ^  설명                                        ^  예시  ^
 +|  +T        | ? extends T  |  Covariance (공변성)              |  **X[T<sub>sub</sub>]**는 **X[T]**의 Sub type  |
 +|  -T          ? super T      Contravariance (반공변성)    **X[T<sup>super</sup>]**는  **X[T]**의 Sub type  |
 +|            T                    Invariance (무공변성)            **X[T]**는 **X[T]**. X[T<sub>sub</sub>] 혹은 X[T<sup>super</sup>]가 X[T]를 대체할 수 없음. |
 +
 +  * Effective Java에서 나오는 [[https://www.oracle.com/technetwork/server-storage/ts-5217-159242.pdf|PECS (Producer extends, Consumer super)]] 원리와 같은 개념.
 +      * Producer는 getter, Consumer는 setter
 +      * Scala의 [[https://www.scala-lang.org/api/2.12.1/scala/Function1.html|Function1]] 혹은 Java의 [[https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html|Function]]을 보면 쉽게 유추 가능.
 +          * 첫번째 인자 **-T1**는 Producer, 두번째 인자 **+R**는 반환값으로 Consumer.
 +              * 반환해야 하는(생산하는) 값이 Super type인데, 인자로 받은(제공하는) 값이 Sub type이면 Super type의 Method를 호출할 수 없음.
 +              * 이런 이유로 T1은 동등한 레벨의 타입이거나 상위 타입(Super type)이어야 한다.
 +          * "상위 추상화 타입 -> 하위 구체화 타입"
 +
 +<sxh scala>
 +// https://github.com/deanwampler/programming-scala-book-code-examples/blob/master/src/main/scala/progscala3/objectsystem/variance/FunctionVariance.scala
 +
 +class CSuper                { def msuper() = println("CSuper") }
 +class C      extends CSuper { def m()      = println("C") }
 +class CSub   extends C      { def msub()   = println("CSub") }
 +
 +object AbsTypes extends App {
 +  // 추상적인 것 -> 구체적인 것
 +  val f1: C => C = (c: C)      => new C
 +  val f2: C => C = (c: CSuper) => new CSub
 +  val f3: C => C = (c: CSuper) => new C
 +  val f4: C => C = (c: C)      => new CSub
 +  
 +  // Error!
 +  /** 인자로 C를 받도록 타입을 정의했는데, C를 상속받은 CSub를 인자로 받는다면
 +    C가 알지못하는 Method가 호출될 수 있다!
 +   */
 +  val f5: C => C = (c: CSub)   => new CSuper
 +}
 +</sxh>
 +
 +
 +
 += Abstract types =
 +
 +https://docs.scala-lang.org/tour/abstract-type-members.html
 +
 +<sxh scala ; highlight:[7,8,13,15,21,23]>
 +import java.io._
 +import scala.io.Source
 +
 +// https://github.com/deanwampler/programming-scala-book-code-examples/blob/master/src/main/scala/progscala3/typelessdomore/AbstractTypes.scala
 +// 위 소스를 응용한 예제
 +abstract class BulkReader {
 +  type In // 구체적인 타입을 지정하지 않음.
 +  val source: In // 바로 위에서 지정한 Type인 "In"을 사용
 +  def read: String
 +}
 +
 +class StringBulkReader
 +(val source: String)  // 추상 타입의 실질 타입 정의
 +  extends BulkReader {
 +  type In = String // 추상 타입 구체화
 +
 +  override def read: String = source
 +}
 +
 +class FileBulkReader
 +(val source: File) // 추상 타입의 실질 타입 정의
 +  extends BulkReader {
 +  type In = File  // 추상 타입 구체화
 +
 +  override def read: String = {
 +    val source1 = Source.fromFile(source)
 +    try source1.mkString finally source1.close()
 +  }
 +}
 +</sxh>
 +
 +== Parameterized types vs. Abstract types ==
 +
 +어떤 상황에서 무엇이 적절한가?
 +
 +  *  Parameterized types : Type parameter가 Parameterized types와 관련이 없는 경우
 +    * List[A]에서 A는 String, Int 무엇이든 상관 없음.
 +  
 +  * Abstract types : 타입 멤버가 객체의 **동작**과 일치하는 경우
 +      * 위 예시를 보면 "읽는다"라는 동작이 일치한다.
 +
 +
 += Implicit Parameters and Conversions =
 +
 +== Implicit Parameters ==
 +
 +Currying 혹은 Lazy 된 함수의 인자를 명시적으로 지정하지 않아도 자신의 네임스페이스의 기본값을 사용하는 방법. 변수나 함수 매개변수에 "implicit"를 앞에 붙인다.
 +
 +"Dependency Injection"의 시각으로 볼 수 있다.
 +
 +
 +<sxh scala>
 +object Printer {
 +  def print(num: Double)(implicit format: String) = println(format.format(num))
 +}
 +
 +// fmt가 주입된다.
 +object Practice extends App {
 +  implicit val fmt = "%.7f"
 +  Printer.print(1.11)
 +}
 +</sxh>
 +
 +
 +== Implicit Class ==
 +
 +암시적 Type 변환. 어느 Instance의 Type이 특정 Instance의 Type인 것처럼 자동 전환하여 그 특정 Instance의 Method를 가진 것처럼 보이게 할 수 있다.
 +
 +C#, Kotlin의 Extension Methods과 비슷한 기능.
 +
 +서로를 고려하지 않고 독립적으로 개발된 소프트웨어/라이브러리를 하나로 묶으려고 할 때 사용.
 +
 +* 생성 규칙
 +  * object/class/trait 안에 정의되어야 한다.
 +  * Implicit를 선언하지 않는 인자를 받아야 한다.
 +  * 같은 namespace에서 object/class/trait 이름이 같아서는 안된다.
 +    * case Class는 사용할 수 없다. (자동으로 생성되는 Companion Object 때문)
 +
 +<sxh scala ; title:Case1_문제제기 ; highlight:[7,8]>
 +object Client extends App {
 +  val uc = new UnaryCalculator(2)
 +  
 +  val op1 = uc ** 3
 +  //val op1 = uc.**(3)
 +
 +  val op2 = 3 ** uc // Error!
 +//val op2 = {3}.**(uc), 정수 3은 ** Method를 가지고 있지 않음.
 +  
 +  println(op1 + " / " + op2)
 +}
 +
 +class UnaryCalculator (val v: Int) {
 +  def ** (`pow`: Int) = math.pow(v, `pow`)
 +}
 +</sxh>
 +
 +<sxh scala ; title:Case1에_대한_해결_방법 ; highlight:[2,16]>
 +object Client extends App {
 +  implicit def intToUnaryCalculator (initVal: Int) = new UnaryCalculator(initVal)
 +
 +  val uc = new UnaryCalculator(2)
 +  
 +  val op1 = uc ** 3
 +  //val op = uc.**(3)
 +
 +  val op2 = 3 ** uc
 +  //val op = {3}.**(uc)
 +
 +  println(op1 + " / " + op2) // 8.0 / 9.0
 +}
 +
 +class UnaryCalculator (val v: Int) {
 +  def ** (`pow`: UnaryCalculator) = math.pow(v, `pow`.v)
 +}
 +</sxh>
 +
  
language/scala.1612207092.txt.gz · Last modified: 2021/02/07 03:15 (external edit)