5. 기본 타입과 연산

dev.jhjhj·2021년 8월 18일
0

Scala

목록 보기
4/10

5.1 기본타입

  • java.lang 패키지에 있는 String 을 제외하면 모든 타입은 scala 패키지의 멤버

5.2 리터럴

  • 기본 타입은 모두 리터럴(literal)로 적을 수 있다.
정수 리터럴
  • Int, Long, Short, Byte에 사용
  • 10진수, 16진수 리터럴이 있다.
부동소수점 리터럴
  • 0~9 로 이루어져있다.
  • 소수점이 있을 수 있으며, 마지막에 E나 e 다음에 지수 부분(exponent)이 있을 수 있다.
    ex ) 1.2345e1은 1.234에 10^1 을 곱한 12.345 이다.
문자 리터럴
  • 작음따옴표 안에 유니코드 문자를 넣어서 만든다.
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

5.3 문자열 인터폴레이션

스칼라는 문자열 인터폴레이션을 위한 유연한 메커니즘을 포함

  • 문자열 리터럴 내부에 표현식을 내장시켜 간결하고 읽기 쉬운 코드 작성 가능
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.
  • 문자열 인터폴레이터 사용 방법 : 문자열 시작하는 따옴표 직전 s를 붙인다.
  • s 인터폴레이터는 내장된 각 표현식( ${} 내부식 )을 평가하고 내장된 표현식을 toString의 결과로 대치해준다.

스칼라는 raw와 f라는 두 가지 인터폴레이터 추가 제공

  • raw 문자열 인터폴레이터
    * s처럼 작동하지만, 문자열 이스케이프 시퀀스 인식 못함
  • f 문자열 인터폴레이터
    * 내장된 표현식에 대해 printf 함수 스타일의 형식 지정을 사용 가능
    ex) 내장시킨 식 바로 다음에 %를 넣고, 그 뒤에 형식 지정 명령 추가
println(raw"No\\\\escape!")
println(f"${math.Pi}%.5f")
===============================
NO\\\\escape!
3.14159

5.4 연산자는 메소드

기본 타입의 풍부한 연산자 제공
예를들어, 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
  • 전위 연산자로 쓰일 수 있는 식별자는 (+, -, !, ~) 4가지 뿐
// 전위 연산자
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!!

5.5 산술 연산

  • 모든 수 타입에 대해 더하기(1), 빼기(-),곱하기(★),나누기(/),나머지(%)를 중위 연산자 를 사용 가능

5.6 관계 연산과 논리 연산

  • 수 타입을 크다(〉),작다(<),크거나 같다(>=),작거나 같다(이라는 관계 연산자를 사용 해 비교 가능, 각 연산자는 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절에서 설명한다.

5.7 비트 연산

  • 비트 연산자는 비트곱(&), 비트합(|), 비트 배타합(^)이 있다.
  • 단항 비트 반전 연산자(~)는 피연산자의 각 비트의 0과 1을 반대로 바꾼다.

  • 스칼라의 shift 메소드 3가지 제공
    (왼쪽 시프트나 부호 없는 오른쪽 시프트는 시프트를 하면서 비트가 밀려난 빈 자리에 0을 채워 넣음)
    - 왼쪽 쉬프트 <<
    - 오른쪽 쉬프트 >>
    - 부호없는 오른쪽 쉬프트 >>>
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

5.8 객체 동일성

두 객체가 같은 지 비교 ==, !=
스칼라는 == 를 사용하면, 먼저 좌항이 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는 자바 객체에 직접 맵핑한 객체에만 사용 가능

5.9 연산자 우선순위와 결합 법칙


위의 표는 메소드 첫 글자에 따른 연산자의 우선순위를 높은 쪽부터 낮은 쪽으로 보여준다.
(같은 줄에 있는 연산자는 우선순위가 같다.)

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

  • 괄호를 사용하여 명확하게 해주는 것이 가장 좋다.

5.10 풍부한 래퍼


profile
어제보다 더 나은

0개의 댓글