스칼라 맛보기 - Tour of Scala

스칼라 맛보기 - Tour of Scala

Apr 27, 2018    

스칼라 공식 웹사이트 - Tour of Scala: Basics 내용을 보고 작성한 포스트입니다.

스칼라 언어의 특징

  • 객체지향: 모든 값이 객체이고, 객체의 타입과 행위는 클래스와 트레잇으로 나타낸다.
  • 함수형: 모든 함수가 값이고 익명 함수, 고차 함수, 중첩, 커링 등을 지원한다.
  • 정적 타입: 추상화를 정적으로 강제하는 타입 시스템을 가지고 있다.
  • 높은 확장성

기본적인 문법

Expressions

스칼라의 표현식은 계산 가능한 명령문이다.

1 + 1 // 2

println을 통해 표현식을 출력할 수 있다.

println(1) // 1
println(1 + 1) // 2
println("Hello!") // Hello!
println("Hello, " + "world!") // Hello, world!

Values

val 키워드를 사용해서 표현식의 결과에 이름을 붙일 수 있다.

val x = 1 + 1
println(x) // 2

위 x와 같이 명명된 결과를 value라 부른다. 참조된 값은 다시 계산할 수 없다. Value는 다시 할당할 수 없다.

val x = 3
x = 2 // 컴파일되지 않는다.

value의 타입은 추론할 수 있지만, 명시적으로 선언이 가능하다.

val x: Int = 1 + 1

Blocks

표현식들을 괄호{}로 감싸서 조합할 수 있다. 우린 이 것을 블록(block)이라 부른다. 블록의 마지막 표현식의 결과가 전체 블록의 결과가 된다.

println({
  val x = 1 + 1
  x + 2
})

val y = {
  val y = 3
  y * 2
}

함수(Functions)

함수는 매개변수를 가지는 표현식이다. 아래와 같이 매개변수에 1을 더한 값을 반환하는 익명함수를 정의할 수 있다.

(x: Int) => x + 1
val z = ((x: Int) => x + 1)(3)

=> 기호 왼쪽엔 매개변수 리스트가 있다. 우측엔 매개변수와 관련된 표현식이 있다. 함수에 이름을 붙일 수 있다.

val addOne = (x: Int) => x + 1
println("1 + 1 = " + addOne(1))

혹은 매개변수 없이 함수 선언이 가능하다.

val getEightyEight = () => 88
println(getEightyEight() + "서울 올림픽")

Methods

메서드는 함수와 생김새와 행동이 매우 유사하다. 그러나 이 둘 사이엔 몇 가지 차이점이 존재한다. 메서드는 def 키워드로 정의된다. def 뒤엔 이름, 매개변수 리스트, 반환 타입, 그리고 본문이 따라와야 한다.

def add(x: Int, y: Int): Int = x + y
add(1, 2)

반환 타입이 매개변수 뒤에 : Int로 선언되었음을 확인해라

메서드는 복수의 매개변수 리스트를 취할 수 있다.

def addThenMultiply(x: Int, y: Int)(mul: Int): Int = (x + y) * mul
addThenMultiply(1,2)(3)

혹은 매개변수 없이 선언이 가능하다.

def name: String = System.getProperty("user.name")
println("Hello, " + name + "!!")

지금까지 Function과의 차이점에 대해 알아봤다. 이제부터 함수와 유사한 특징을 알아보자 메서드는 여러 줄에 걸친 표현식을 가질 수 있다.

def getSquareString(input: Double): String = {
  val square = input * input
  square.toString
}

본문의 마지막 표현식이 메서드의 반환값이다. (Scala는 return 키워드가 있지만, 거의 사용되지 않는다.)

Classes

class 뒤에 이름, 생성자 매개변수와 함께 클래스를 정의하는 것이 가능하다.

class Hello(prefix: String, suffix: String) {
  def greet(name: String): Unit =
    println(prefix + name + suffix)
}

greet 메서드의 반환 타입은 Unit이다. 이 것은 반환할 값이 없다는 것을 의미한다. 이는 Java나 C에서의 void와 유사하다. (차이점은 모든 스칼라 표현식은 값을 가져야 하기 때문에, ()로 쓰여진, Unit 타입의 싱글턴 값을 반환한다. 이는 아무런 정보가 없다.)

new 키워드를 사용해 클래스의 인스턴스를 생성할 수 있다.

val greeter = new Hello("Hello, ", "!")
greeter.greet("Scala Beginner")

Case Classes

스칼라는 case라 부르는 클래스의 특별한 타입이 존재한다. 기본적으로, Case 클래스들은 불변이고 값에 의해 비교된다. case class 키워드를 통해 정의할 수 있다.

case class Point(x: Int, y: Int)

new 키워드 없이 Case Class의 인스턴스를 생성할 수 있다.

val point = Point(1, 2)
val anotherPoint = Point(1, 2)
val yetAnotherPoint = Point(2, 2)

그리고 값에 의해 비교할 수 있다.

if(point == anotherPoint) {
  println(point + "와 " + anotherPoint + "는 같다.")
} else {
  println(point + "와 " + anotherPoint + "는 다르다.")
}
// Point(1,2)와 Point(1,2)는 같다.
if(point == yetAnotherPoint) {
  println(point + "와 " + yetAnotherPoint + "는 같다.")
} else {
  println(point + "와 " + yetAnotherPoint + "는 다르다.")
}
// Point(1,2)와 Point(2,2)는 다르다.

Objects

Object는 자신만의 정의를 가진 싱글 인스턴스이다. 자신만의 클래스를 가진 싱글턴 객체로 생각할 수 있다. object 키워드를 통해 정의한다.

object IdFactory {
  private var counter = 0
  def create(): Int = {
    counter += 1
    counter
  }
}

참조된 이름을 통해 object에 접근 가능하다.

val newId: Int = IdFactory.create()
println(newId) // 1
val newerId: Int = IdFactory.create()
println(newerId) // 2

Traits

Trait은 특정 필드와 메서드를 포함한 타입이다. 여러 trait들을 조합할 수 있다. trait 키워드를 통해 trait을 정의할 수 있다.

trait Greeter1 {
  def greet(name: String): Unit
}

trait은 기본적으로 구현이 가능하다.

trait Greeter {
  def greet(name: String): Unit =
    println("Hello, " + name)
}

extends 키워드를 통해 trait을 확장하고 override 키워드를 통해 재구현이 가능하다.

class DefaultGreeter extends Greeter

class CustomizableGreeter(prefix: String, postfix: String) extends Greeter {
  override def greet(name: String): Unit = {
    println(prefix + name + postfix)
  }
}

val defaultGreeter = new DefaultGreeter()
defaultGreeter.greet("Scala Developer")

val customGreeter = new CustomizableGreeter("How are you, ", "?")
customGreeter.greet("Scala Developer")

DefaultGreeter 는 오직 하나의 트레잇을 상속받았지만, 여개의 trait을 상속받을 수 있다.

Main Method

메인 메서드는 프로그램의 입구이다. JVM은 main이란 이름을 가진 메인 메서드와 스트링 배열로 구성된 하나의 매개변수를 필요로한다. object를 사용해서, 아래와 같이 메인 메서드를 정의할 수 있다.

object Main {
  def main(args: Array[String]): Unit =
    println("Hello, Scala beginner!")
}