scala> val a = 'A'
a: Char = A
scala> val d = '\u0041'
d: Char = A
scala〉 val f = '\u0044'
f: Char = D
문자열 리터럴은 큰따옴표 두개("")로 둘러싼 문자
scala> val escapes = "\\\"\'"
escapes: String = V"
println("""Welcome to Ultamix 3000.
Type "HELP" for help.""")
Welcome to Ultamix 3000.
Type "HELP" for help.
// stripMargin을 문자열에 넣으면
println("""|Welcome to Ultamix 3000.
|Type "HELP" for help.""")
// 다음과 같이 출력된다.
Welcome to Ultamix 3000.
Type "HELP" for help.
심볼(Symbol)은 작은 따옴표 + 문자열로 표현한다. ex) 'ident
작음 따옴표 뒤에 오는 식별자는 알파벳, 숫자 혼합 아무거나 가능
심볼 리터럴을 scala.Symbol이라는 클래스의 인스턴스로 매핑
스칼라에서는 선언하지 않은 필드 식별자를 메소드에 전달해 연산을 수행할 수 없다.
대신에, 심볼 리터럴을 넘기면 거의 비슷하게 표현 가능하다.
def updateRecordByName(r: Symbol, value: Any) = {
// 코드
println(r.name)
}
// Symbol을 사용하지 않은 경우 Error
// updateRecordByName(favoriteAlbum, "OK Computer")
// Symbol을 사용하는 경우
updateRecordByName('favoriteAlbum, "OK Computer")
===================================================
favoriteAlbum
// 같은 Symbol 리터럴을 2번 이상 사용하면, 사용된 Symbol은 서로 완전 동일한 객체를 참조한다.(Intern)
val a = 'hi
val b = 'hi
println(a eq b)
================
true
Boolean 타입의 리터럴에는 true와 false
스칼라는 문자열 인터폴레이션을 위한 유연한 메커니즘을 포함
val name = "reader"
println(s"Hello, $name!") // "Hello, " + name + "!"
println(s"The answer is ${6*7}." // "The answer is " + "42" + "."
===============================
Hello, reader!
The answer is 42.
스칼라는 raw와 f라는 두 가지 인터폴레이터 추가 제공
println(raw"No\\\\escape!")
println(f"${math.Pi}%.5f")
===============================
NO\\\\escape!
3.14159
기본 타입의 풍부한 연산자 제공
예를들어, 1+2는 1.(2)와 같다. 다시 말해, Int 클래스에는 Int를 인자로 받아 Int를 결과로 돌려주는 +라는 이름의 메소드가 있다.
// + 연산자
val sum = 1+2
val sumMore = 1.+(2)
=======================
3
3
오버로드를 통해 파라미터 타입을 다르게 한 메소드가 다소 존재
ex ) Int에는 Long을 받고 Long 타입을 반환하는 + 메소드가 존재
// 연산자 오버로드
def +(x: Byte): Int
def +(x: Short): Int
def +(x: Char): Int
def +(x: Int): Int
def +(x: Long): Long
def +(x: Float): Float
def +(x: Double): Double
모든 메소드는 연산자가 될 수 잇다.
스칼라에서 연산자는 문법적으로 특별한 것이 아니다. 어떤 메소드든 연산자가 될 수 있다. 메소드가 연산자 역할을 할지 여부는 프로그래머가 메소드를 사용하는 방법에 따라 결정된다.
스칼라는 중위(infix) 연산자, 전위(prefix), 후위(postfix) 연산자 존재
전위와 후위는 중위에 다르게 피연산자가 하나이다. 이는 단항(unary) 연산자를 뜻한다.
단항 연산자도 실제로 메소드 호출을 간략하게 한 것
// 단항연산자
print(-7) // 스칼라는 7.unary_-를 호출한다.
=======================
-7
// 전위 연산자
def main(args: Aarray[String]): Unit = {
val test = new Test(5)
println(+test)
println(-test)
println(*test) // not found Error
}
class Test(val num: Int) {
def unary_+(): Int = if(num > 0) num else -num
def unary_-(): Int = if(num > 0) -num else num
def unary_*(): Int = num * num
}
val s = "Hello, world!!"
println(s)
println(s.toLowerCase) // 괄호 생략
println(s toLowerCase) // (.) 생략
=================================
Hello, world!!
hello, world!!
hello, world!!
수 타입을 크다(〉),작다(<),크거나 같다(>=),작거나 같다(이라는 관계 연산자를 사용 해 비교 가능, 각 연산자는 Boolean 값을 결과로 내놓고, '!' 연산자를 사용해 Boolean 값을 반전시킬 수 있다.
논리 연산으로는 논리곱 ( &&, & ), 논리합( ||, | )이 있다. 각각은 두 Boolean 피연산자를 취하며 Boolean 결과값을 반환한다.
논리연산은 Java와 마찬가지로 숏 서킷 연산이다. 논리연산자로 구성된 표현식은 결과를 결정하기 위해 필요부분만 값을 계싼
ex ) && 연산에서 왼쪽의 표현식이 false이면, 리턴값을 false이기 때문에 오른쪽 표현식은 계산 X
def main(args: Aarray[String]): Unit = {
left() && right() // 이미 left()에서 false라는 결과가 도출되었기 때문에 right() 메소드는 호출되지 않는다.
left() & right() // 좌항의 값과 관계없이 우항의 표현식을 항상 평가하고 싶다면, &와 |를 대신 사용하면 된다.
}
def left() = {
println("left")
false
}
def right() = {
println("right")
true
}
=======================================
left
left
right
참고 ) 스칼라 메소드에는 인자 계산을 미루는 기능이 있어 쇼트 서킷 연산을 수행할 수 있다. 심지어 사용할 필요가 없으면 인자를 전혀 계산하지 않을 수도 있다. 이런 기능을 이름에 의한 호출 ( call by name ) 파라미터라 부르며, 9.5절에서 설명한다.
println(1&2) // 1(0001) 2(0010) -> 0(0000)
println(1|2) // 1(0001) 2(0010) -> 3(0011)
println(1^3) // 1(0001) 3(0011) -> 2(0010)
println(~1) // 1(0001) -> 11111111111111111111111111111110
println(-1 >> 31) // -1을 오른쪽으로 31비트만큼 이동한다.
println(-1 >>> 31) // -1을 오른쪽으로 시프트하면서 0을 채워넣는다.
=======================================
0
3
2
-2
-1
1
두 객체가 같은 지 비교 ==, !=
스칼라는 == 를 사용하면, 먼저 좌항이 null인지 검사, 좌항이 null이 아니라면 해당 객체의 equals 메소드를 호출
println(1==2)
println(1!=2)
println(List(1,2,3) == List(1,2,3)) // 모든 객체에 적용할 수 있다.
println(List(1,2,3) == List(4,5,6))
println(1==1.0) // 타입이 각기 다른 두 객체도 비교할 수 있다.
println(List(1,2,3) == "hello")
println(List(1,2,3) == null) // null 객제와도 비교할 수 있다.
=======================================
false
true
true
false
true
false
스칼라의 ==는 자바와 어떻게 다른가 ?
자바에서 ==는 참조타입과 원시타입 비교 가능
- 자바의 ==은 원시타입에서 값이 같은지 비교, 참조 타입은 참조가 같은지(즉, heap) 메소리에서 같은 객체를 가리키고 있는지 비교
스칼라의 참조 동일성 검사 기능은 eq, 그 역은 ne 이다. 하지만 eq와 ne는 자바 객체에 직접 맵핑한 객체에만 사용 가능
위의 표는 메소드 첫 글자에 따른 연산자의 우선순위를 높은 쪽부터 낮은 쪽으로 보여준다.
(같은 줄에 있는 연산자는 우선순위가 같다.)
print(2 << 2 + 2) // 2 << (2+2)
print(2 + 2 << 2) // (2+2) << 2
===============================
32
16
우선순위 규칙의 한가지 예외는 할당 연산자(=)이다.
어떤 연산자가 등호로 끝나고, 그 연산자가 비교 연산자( <=, >=, ==, != )가 아니면 해당 연산자의 우선순위는 다른 모든 연산자보다 우선순위가 낮다.
val x *= y+1 // x *= (y+1)
우선순위가 같은 연산자가 표현식에서 나란히 나올 경우 결합법칙을 통해 어떻게 연산자를 묶을지 결정한다.
메소드 이름이 ':'으로 끝나면 오른쪽부터 왼쪽으로, ':'이 아닌 글자로 끝나면 왼쪽에서 오른쪽으로 짝을 이어간다.
a ::: b ::: c → a ::: ( b ::: c )
a b c → ( a b ) c
괄호를 사용하여 명확하게 해주는 것이 가장 좋다.