[Kotlin] 코틀린의 변수 타입과 제어문

Rupee·2023년 3월 21일
0
post-thumbnail

☁️ 변수 다루기

1. 자동 타입 지정

  1. var : 가변 변수
  2. val : 불변 변수, 자바에서의 final 키워드가 붙은 것과 같음
var number1 = 10L  // long number1 = 10L;
val number2 = 10L // final long number1 = 10L;

2. 수동 타입 지정

val number1: Long = 10L

만약 초기값을 지정해 주지 않는다면, 컴파일 에러가 나기 때문에 무조건 수동으로 타입을 지정해주어야 한다. 값을 사용하기 전에 초기화 되어야 하기 때문이다.

또한 리스트와 같은 val 컬렉션이 있더라도 변수의 재할당은 불가능 하지만 , 원소의 추가 및 삭제는 가능한 것을 볼 수 있다.

모든 변수는 기본 값 val

원시 타입 VS 참조 타입

코틀린은 박싱과 언박싱을 고려하지 않아도 된다.

즉 코틀린에서는, 원시 타입과 참조 타입을 표면적으로 구분하지 않는다. 자바에서 Longlong 이 다르지만, 코틀린에서는 겉으로는 Long 만 사용하며 자체적으로 상황에 따라 필요한 경우에 원시 타입으로 박싱과 언박싱을 자동으로 해준다.

🔖 NULL 대입하기
변수에 null 을 넣고 싶다면 타입 물음표를 지정해주면 된다.

var number1: Long? = 1,00L

객체 인스턴스화

코틀린에서는 객체 인스턴스화 할 때 new 를 붙이면 안된다.

var person = Person("a");  // Person person = new Person("a);

☁️ Null 다루기

인자 값과 반환 값으로 null 들어올 수 있다면, ? 을 통해 명시해주어야 한다.

또한, 코틀린에서는 null 이 들어갈 수 있도록 명시해놓은 상태에서 바로 함수를 호출할 수 없도록 막고 있기 때문에 위에서 null 체크를 한 번 해줘야 컴파일 에러가 안난다.

fun startswith(str: String): Boolean {  // 컴파일 에러 X, 안전한 코드 X
    return str.startswith("A");
}
fun startswith(str: String?): Boolean? { // Null 체크로 안전
	if (str == null) {
    	return null;
    }
    return str.startswith("A");
}
fun startswith(str: String?): Boolean{
    if (str == null) {
    	return false;
    }
    return str.startswith("A");

코틀린은 null 타입을 특별하게 취급하기 때문에, 다음과 같은 부가 기능들을 제공한다.

1. Safe Call

null 값이 아니면 뒤에 딸려오는 프로퍼티를 실행하고, null 값이면 실행하지 않는 방식이다.

val str: String? = "aa"
str.length   // 컴파일 에러
str?.length  // 정상 동작

2. Elvis

null 값인 경우 ?: 를 통해 기본값을 설정해줄 수 있다. if 문을 통해 null 체크를 따로 해주지 않아도 되어 편리하다.

val str? String? = "aa"
println(str?.length ?: 0) // null 인 경우 0 출력, 아니면 길이 출력

이 두 가지 연산자를 활용해서 startswith 함수를 코틀린스럽게 바꿔보면 다음과 같다.

fun startswith(str: String?): Boolean {
	return str?.startsWith("A");  // null 인 경우 null 반환
}
fun startswith(str: String): Boolean {
	return str?.startsWith("A") ?: false  // null 인 경우 false
}

3. Null 타입이 아님을 단언하는 방법

nullable Type 이지만, 어떤 경우에도 null 일 수가 없는 경우 !! 를 사용하여 컴파일러에게 명시해주면 된다. 하지만 null 이 들어올 경우 NPE 예외가 나기 때문에 주의해야 한다.

fun startswith(str: String?): Boolean {
	return str!!.startsWith("A")
}

Optional?
코틀린 Nullable 타입 vs. 자바 Optional

☁️ 코틀린에서 Type 다루기

1. 기본 타입

코틀린은, 선언된 기본 값을 보고 타입을 자동으로 추론한다.

val number = 3.0f  // Float
val number = 3.0  // Double

또한 자바가 암묵적으로 타입 캐스팅을 지원해주는 반면, 코틀린은 명시적으로toString, toDouble 과 같이 타입을 변환해주어야 컴파일 에러가 안난다.

val number1: Int = 3
val number2: Long = number1.toLong()  // 형변환
println(number1 + number2)

2. 타입 캐스팅

기본 타입이 아닌 일반 타입을 변환하려면 어떻게 해야할까? 괄호가 아닌 as 를 사용하면 되며, obj as Personobj 라는 변수를 Person 타입으로 간주한다는 뜻을 담고 있다. 해당 타입으로 변환할 수 없는 경우, 예외가 발생한다.

🔖 as?
안전한 타입 형변환을 할 수 있는 방법이다.
null 타입이 아닐 경우 해당 타입으로 변환해주고, null 일 경우나 해당 타입으로 변환이 불가능할 경우 null 을 반환한다.

fun printAgeIfPerson(obj: Any) {
	if(obj is Person) {    // obj !is Person
    	val person = obj as Person
        println(person.age)
    }
}

다음과 같이 타입 캐스팅을 생략해도 코틀린은 정상 컴파일 된다.

fun printAgeIfPerson(obj: Any) {
	if(obj is Person) {  
        println(obj.age)  
    }
}
public static void printAgeIfPerson(Object object) {
	if(object instance of Person) {
    	Person person = (Person) object;
        System.out.println(person.age);
    }
}

3. Kotlin의 3가지 특이한 타입

  1. Any
    자바의 Object 와 같다. 단, 원시 타입을 포함한 모든 타입의 부모이며 null 까지 포함하려면 any? 를 사용해야 한다.
  2. Unit
    자바의 Void 와 같다. 단, 그 자체로 타입 인자로 사용이 가능하다.
  3. Nothing
    함수가 정상적으로 끝나지 않았다는 것을 표현한다. ex) 무조건 예외를 반환하는 함수

4. String Interpolation / String indexing

자바의 String.format() 을 활용하지 않고, 바로 다음과 같은 형태로 사용이 가능하다.

val person = Person("최태현")
println("이름 : ${person.name}")

또한 자바에서 String 타입에 대해 인덱싱은 String.charAt() 을 사용해서 가능했던 것과 달리 코틀린에서는 바로 []로 인덱싱이 가능하다.

val str = "ABC"
println(str[0])

☁️ 연산자

1. 객체 간의 연산

자바에서 Comparable 인터페이스를 구현해서 compareTo 메서드를 직접 호출해줬던 것과 달리, 코틀린에서는 객체 간의 비교 연산이 있다면 자동으로 메서드를 호출해준다.

val money1 = Money("2000")
val money2 = Money("1000")
if (money1 > money2) {  // money1.compareTo(money2) X
   ...
}

2. 동일 연산자와 동등 연산자

  1. 자바 : 동일성 비교에 == , 동등성 비교에 equals 사용
  2. 코틀린 : 동일성 비교에 ===, 동등성 비교에 == 를 사용한다. == 를 사용하면 코틀린이 equals() 를 자동으로 호출한다.

☁️ 제어문

1. if-else

자바에서 if-elsestatement 로 간주되어 변수에 대입하면 컴파일 에러가 나지만, 코틀린에서는 이를 experssion 으로 간주하기 때문에 바로 변수에 대입할 수가 있다.

따라서, 코틀린에서는 삼항 연산자가 존재하지 않는다.

🔖 Statement VS Expression
statement : 프로그램 문장, 하나의 값으로 도출되지 않는다.
expression : 하나의 값으로 도출되는 문장이다.

fun getPassOrFail(score: Int): String{
	return if (score >= 50) {
       "P"
    } else {
       "F"
    }
}
if (score !in 0..100) {  // 0 <= score && score <= 100
   ...
}

2. Switch와 When

복잡한 Switch-case 문이나 if-elseWhen을 사용하여 다음과 같이 간단하게 바꿀 수 있다.

fun judgeNumber(num: Int) {
	when(number) {
    	1, 0, -1 -> println(..)  // if (number == 1 || number == 0 || number == -1)
        else -> println(..)
    }
}

when 함수 안에 값이 있다면 그 값을 기준으로 해서 조건을 살펴보고, 값이 없는 경우에는 조건 자체만 살펴본다.

☁️ 반복문

1. for each 문

val numbers = listOf(1L, 2L, 3L)  // Arrays.asList(1L, 2L, 3L)
for (number in numbers) {  // for (long number : numbers)

}

2. 전통적인 for 문

  1. n 칸씩 올라가는 경우
    .. 은 범위를 만들어내는 연산자이며, step 을 이용해 몇 칸씩 건너 뛰면서 탐색할 지 결정할 수 있다.

    for (i in 1..5 step 2) {  // step은 중위 함수
    }  
  2. n 칸씩 내려가는 경우

    for (i in 3 downTo 1) {  // downTo는 중위 함수
    
    }

사실, .. 연산자는 IntRange 라는 클래스이고, 해당 클래스는 Progression 클래스를 상속받고 있다. 즉, 등차수열(연속하는 두 항의 차이가 모두 일정한 수열)을 만들어주고 있는 것이다.

🔖 참고: 중위함수
변수.함수 이름(인자) -> 변수 함수이름 인자

☁️ 예외 다루기

1. try catch finally

fun parseIntOrthrow(str: String) Int {
  try {
    return str.toInt()
  } catch(e: NumberFormatException) {
    throw IllegalArgumentException("주어진 ${str} 는 숫자가 아닙니다)
  }
}

if-else 처럼 try-catchExpression 으로 간주되기 때문에, return 을 한번만 사용할 수 있다.

fun parseIntOrThrow2(str: String) Int? {
   return try {
      str.toInt()
   } catch () {
      null
   }
}

2. Checked Exception / Unchecked Exception

코틀린에서는 둘을 구분하지 않는다.
즉, 모두 Unchecked Exception으로 간주하기 때문에 Checked Exception 인 경우에도, 메서드 시그니처에 throws 를 붙이지 않아도 컴파일 에러가 나지 않는다.

2. try-with resources

괄호 안에서 외부 자원을 만들어 주고, 괄호 안의 내용이 끝나면 자동으로 외부 리소스를 반납해주는 구문이다.

하지만 코틀린에서는 try-with resources 구문이 없고 대신 use 라는 인라인 함수를 사용한다.

fun readFile(path: String) {
	BufferedReader(FileReader(path)).use { reader ->
    	println(reader.readLine())
    }

☁️ 함수 다루기

기본 문법

함수가 하나의 결과값이라면, 블럭 대신 = 를 사용 가능하다.

{return 를 없애고 = 를 사용하여 중괄호 안에서 리턴 값으로 어떤 것을 준다는 표현 대신 "해당 함수의 값은, 해당 로직의 결과물이야"라는 것을 간단하게 표현할 수가 있다.

fun max(a: Int, b: Int): Int = 
	if (a > b) {    // return if (a > b)
    	a
    } else {
        b
    }
}

참고로 = 를 사용하면 반환 타입을 생략할 수 있으므로 다음과 같이 간단하게 리팩토링 할 수 있다(한 줄로 변경 + 타입 자동 추론 + 중괄호 삭제).

fun max(a: Int, b: Int): if (a > b) a else b

Default parameter

자바에서는, 메서드 오버로딩을 통해 메서드 파라미터의 기본값을 지정할 수 있다. 하지만 이런 경우 만들어야 하는 메서드 개수가 증가한다는 단점이 존재한다.

public void repeat(String str, int num, boolean useNewLine) {
	for (int i = 1; i <= num ; i++) {
    	if (userNewLine) {
        } 
    }
}

public void repeat(String str, int num) {
	repeat(str, num, true);
}

따라서 코틀린에서는 default parameter 를 지원한다. 밖에서 파라미터를 넣어주지 않는 경우, 지정한 기본값을 자동으로 사용한다.

fun repeat(
	str: String, 
    num: Int = 3,
    userNewLine: Boolean = True
){
    for (i in 1..num) {
    ...
    }
}

Named argument(parameter)

함수 호출 시 특정 파라미터만을 지정해 넣어 줄 수 있다.
매개변수 이름을 통해 직접 지정할 수 있으며, 지정되지 않은 매개변수는 기본값을 사용하게 된다.

또한 Named argument빌더를 직접 만들지 않고도 빌더의 장점을 가질 수 있다. 파라미터에 이름을 명시하면은 다수의 파라미터일 때 위치를 바꿔서 작성한다던지 등의 문제를 막을 수 있기 때문이다.

fun main() {
   // repeat("Hello World", 3, false)
   repeat("Hello World", useNewLine = false)
}

가변 인자

vararg 를 파라미터 앞에 붙여주면 되며, 자바와 다른 점은 배열의 경우 배열 앞에 * 를 넣어주어야 한다.

fun main() {
	val array = arrayOf("A", "B", "C")
    printAll(*array)
    printAll("A", "B", "C")
}

fun printAll(vararg strings: String) {
	for (str in strings) {
    }
}

출처
자바 개발자를 위한 코틀린 입문(Java to Kotlin Starter Guide)

profile
개인용으로 공부하는 공간입니다. 잘못된 부분은 피드백 부탁드립니다!

0개의 댓글