2022. 10. 28. 00:35ㆍDev/Scala
Tour of Scala 시리즈의 글은 다중패러다임(함수형 + OOP) 언어인 Scala 의 공식 튜토리얼 'Tour of Scala 문서를 톺아보고 학습해보는 글이다. 내용에 따라서 추가된 내용과 생략된 부분이 있다. (중요) 또한 학습은 Scala 3 을 기준으로 작성되었다. 몇몇 부분에서 2버전과는 다른 내용으로 진행하였다.
- 공식 web site : https://www.scala-lang.org
- Tour of Scala : https://docs.scala-lang.org/tour/tour-of-scala.html
Expressions
values / variables
다른 언어에도 동일한 개념이 있는 '상수(value)' 와 '변수(variable)'. 이 둘을 구분 짓는 가장 기본적인 개념은 re-assign 의 가능여부다.
re-assign 이 가능하면 변수, 불가능하면 상수.
val x = 1 // assign to 'value' x
x = 2 // re-assign 'value' : compile error
var y = 200 // assign to 'variable' y
y = 0 // re-assign : OK
println(y) // 0
val 과 var 는 assign 되는 값의 타입을 바탕으로 유추하여 결정되고 한번 정해진 타입은 다른 타입으로 변경될 수 없다. 또한 명시적으로 지정할 수도 있다.
var x = 0 // 변수 x 에 int 0 이 assign 되면서 x 의 type 은 Int 가 되었다
x = "ok" // compile error! : 한번 assign 된 type 에 다른 type 을 assign 할 수 없다.
var y: Int = 0 // 명시적 type 지정
Blocks
여러 라인의 수식을 묶어둘때 `{ }` 을 이용하는데 가장 마지막 줄이 이 block의 result 가 된다.
println({
val x = 1
val y = 2
x + y // result of block : 3
})
// block 을 value 로 assign 해서 사용할 수도 있다.
val blockResult = {
val x = 1
val y = 2
x + y
}
println(blockResult) // result of value 'blockResult' : 3
또한 기본적으로 다른언어에서도 공통적으로 가지고 있는 value/variable 의 scope 를 제한하는 역할도 한다.
Functions
Functions are expressions that have parameters, and take arguments.
우리는 먼저 'parameter' 와 'argument' 에 대해서 먼저 간단하게 구분해야 function 에 대한 개념이 설 것 같다. 그렇지 않으면 위의 예제에서 단순히 blocks 로 감싼 blockResult value 와 혼동될 수도 있으니.
- parameter : function signature에서 입력받은 값을 가르키기 위해 정의한 변수
- argument : 미리 정의된 function 을 실행할때 넘겨주는 실제 값
val addOne = (x: Int) => x + 1 // x is parameter
val result = addOne(99) // 99 is argument
함수란, 정의된 parameters 로 전달된 arguments 를 취하는 수식이다. 그 모양은 아래와 같다
val functionName = (parameter name: type) => expressions body
(x: Int) => x + 1 // 익명함수
val addOne = (x: Int) => x + 1 // 함수 이름 지정 가능
val addTwo = (x: Int) => { // block 사용 가능
println("x is : " + x)
x + 2 // 마지막 줄이 이 함수의 result!
}
val addThree = (x: Int) => // block 없이 들여쓰기 만으로도 표현이 가능(like python)
println("x is : " + x)
x + 3
val minus = (x: Int, y: Int) => x - y // 여러개의 파라미터 정의
val hundred = () => 100 // 파라미터 없이도 가능
Methods
일반적으로 함수(function) 의 하위 개념으로 method 가 존재한다. method 는 클래스에 종속된 함수다. 개념적으로는 이렇게 구분되지만 코드를 작성함에 있어서는 동일하게 작성 하게 되지만, scala 에서는 method 를 작성하는법이 별도로 존재한다.
def method이름(파리미터1, 파라미터2)(또다른 파라미터): 반환타입 =
// ...body...
// ...body...
// 마지막 줄은 이 method 의 결과 반환값
- 시작할때 keyword 'def' 로 시작한다.
- parameter 는 다수의 () 를 허용한다. (예제로 확인)
- body 는 = 로 구분된다.
- 반환형(return type) 을 명시할 수 있다.
def addOne(x: Int): Int = x + 1
def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier
Classes
scala 에도 class가 있고 이를 작성하는 법은 조금 독특하다. (이는 kotlin 의 매개변수 생성방법과 유사하다)
이때 정의된 파라미터는 val(value) 이므로 class instance가 생성되면 변경할 수 없다.
class 클래스명(파라미터1, 파라미터2 ...):
class Greeter(prefix: String, suffix: String):
def greet(name: String): Unit =
println(prefix + name + suffix)
Case Classes
case class 는 scala 의 매우 독측한 타입으로 기본적으로 immutable 이며 클래스의 비교시에 value 를 비교하게 된다 (case 가 붙지 않은 보통의 class 는 단순 비교시 instance 의 참조값을 비교한다). 여담이지만 case class 를 보면서 data를 담을 훌륭한 타입이라고 생각한다.
작성법은 기본 class 작성법과 유사하고 앞에 keyword 'case' 가 붙고, body 가 없기 때문에 마지막에 ':'(콜론)을 붙히지 않는다.
case class 클래스명(파라미터1, 파라미터2...)
case class Pointer(x: Int, y: Int)
val temp1 = Pointer(1, 2)
val temp2 = Pointer(1, 2)
val temp3 = Pointer(100, 2)
println(temp1 == temp2) // true
println(temp1 == temp3) // false
Objects
단 하나의 instance 만을 가지는 singleton class 이다.
object Util:
private var x = 0
def setX(tempX: Int): Unit =
this.x = tempX
def getX: Int = this.x
val util = Util // 실제 reference 가 같다
val anotherUtil = Util // 실제 reference 가 같다
println(util.getX) // 0
util.setX(100)
println(anotherUtil.getX) // 100
// util 과 anotherUtil 의 reference 가 같기 때문에 한쪽에서 x 의 값을 변경하면 다른 쪽에서도 영향을 받게 된다
Traits
'특성' 이라는뜻이 trait 은 scala 의 추상타입이다. 이는 Java 의 interface 와 매우 유사하다.
- 기본적으로 구현해야 할 명세(method)만을 작성한다.
- default 로 method 의 body를 가질 수 있다.
- class 는 단 하나만 extend 할 수 있지만 trait 은 다중으로 extend 할 수 있다.
- 재정의(override) 할 method 는 keyword 'overriede' 가 붙는다. (Java 에서는 명시적으로 keyword 를 붙히지 않는다. annotation(@Override) 으로 대체할 수 있으나 필수는 아니다.)
(이거 완전 Java interface 인데?!)
trait Greeter:
def greet(name: String): Unit =
println("Default : " + name)
class DefaultGreeter extends Greeter
class CustomGreeter extends Greeter:
override def greet(name: String): Unit =
println("Custom : " + name)
val greeter = DefaultGreeter()
greeter.greet("Jay 1")
val customGreeter = CustomGreeter()
customGreeter.greet("Jay 2")
Program Entry Point
작성한 프로그램이 최초 실행시의 진입점. C의 'int main()', Java 의 'public static void main()' 과 같지만 함수의 특정 네이밍이 강제되는 것은 아니다. 대신 annotation '@main' 이 붙은 함수가 그 진입점이 된다.
@main def hello() = println("Hello, Scala developer!")
'Dev > Scala' 카테고리의 다른 글
[Tour of Scala] Unified Types (0) | 2022.11.15 |
---|