포스팅 OS : Mac

검색어 : 코틀린(Kotlin), 스마트 캐스트(Smart Cast)


▶︎ 이전 포스팅 목록

2018/02/10 - [Android_Kotlin] - [Android Kotlin] 안드로이드 스튜디오(Andorid Studio)에 코틀린(Kotlin) 개발환경 셋팅

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 텍스트뷰(TextView)와 버튼(Button)을 사용한 헬로 코틀린

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 함수와 변수를 이용하여 에디트텍스트(EditText)에 입력한 값 중 큰 값 구하기

2018/03/15 - [Android_Kotlin] - [Android Kotlin] 문자열 템플릿(String Template) 사용법

2018/06/05 - [Android_Kotlin] - [Android Kotlin] 클래스, 프로퍼티, 커스텀 접근자 - 라디오 버튼(Radio Button)


이번엔 코틀린의 스마트 캐스트에 포스팅하겠습니다.


코틀린에서는 is를 사용해 타입검사를 합니다. 자바의 instanceof와 비슷합니다. 하지만 자바에서는 어떤 변수의 타입을 instanceof로 확인한 다음에는 그 타입에 속한 멤버에 접근하기 위해서는 명시적으로 변수 타입을 캐스팅해야 합니다. 하지만 코틀린에서는 프로그래머 대신 컴파일러가 캐스팅을 해줍니다.


즉, is로 검사하고 나면 굳이 변수를 원하는 타입으로 캐스팅하지 않아도 마치 처음부터 그 변수가 원하는 타입으로 선언된 것 처럼 사용할 수 있습니다. 이런 기능을 스마트 캐스트(Smart Cast)라고 부릅니다. 이제 아래 작성한 소스를 보시면 이해가 더 쉽게 되실 것입니다.




1. 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.kotlin_test.Activity.SmartCastActivity">

<TextView
android:text="산술식 : ( a + b ) + c"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:weightSum="6">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="첫 번째 :"
android:gravity="center"/>
<EditText
android:id="@+id/edt_int_01"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:inputType="number"/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="두 번째 :"
android:gravity="center"/>
<EditText
android:id="@+id/edt_int_02"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:inputType="number"/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="세 번째 :"
android:gravity="center"/>
<EditText
android:id="@+id/edt_int_03"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:inputType="number"/>
</LinearLayout>
<Button
android:id="@+id/btn_cal1"
android:text="If 연쇄 계산하기"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"/>

<Button
android:id="@+id/btn_cal2"
android:text="값을 만들어내는 If 계산하기"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"/>
<Button
android:id="@+id/btn_cal3"
android:text="When 사용 계산하기"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"/>
<Button
android:id="@+id/btn_cal4"
android:text="When 블록 사용 계산하기"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"/>
<TextView
android:id="@+id/txt_cal_result"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"
android:gravity="center"/>
<TextView
android:id="@+id/txt_cal_log"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="21sp"
android:gravity="center"/>
</LinearLayout>





2. 소스

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.kotlin_test.R
import kotlinx.android.synthetic.main.activity_smart_cast.*

interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

class SmartCastActivity : AppCompatActivity() {
var log: String = ""

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_smart_cast)

btn_cal1.setOnClickListener {
txt_cal_log.setText("")

val num1: Num = Num(Integer.parseInt(edt_int_01.text.toString()))
val num2: Num = Num(Integer.parseInt(edt_int_02.text.toString()))
val num3: Num = Num(Integer.parseInt(edt_int_03.text.toString()))

val result: Int = eval1(Sum(Sum(num1, num2), num3))
txt_cal_result.setText("If 연쇄 : " + result.toString())
}

btn_cal2.setOnClickListener {
txt_cal_log.setText("")

val num1: Num = Num(Integer.parseInt(edt_int_01.text.toString()))
val num2: Num = Num(Integer.parseInt(edt_int_02.text.toString()))
val num3: Num = Num(Integer.parseInt(edt_int_03.text.toString()))

val result: Int = eval2(Sum(Sum(num1, num2), num3))
txt_cal_result.setText("If 식 : " + result.toString())
}

btn_cal3.setOnClickListener {
txt_cal_log.setText("")

val num1: Num = Num(Integer.parseInt(edt_int_01.text.toString()))
val num2: Num = Num(Integer.parseInt(edt_int_02.text.toString()))
val num3: Num = Num(Integer.parseInt(edt_int_03.text.toString()))

val result: Int = eval3(Sum(Sum(num1, num2), num3))
txt_cal_result.setText("When 사용 : " + result.toString())
}

btn_cal4.setOnClickListener {
log = ""
val num1: Num = Num(Integer.parseInt(edt_int_01.text.toString()))
val num2: Num = Num(Integer.parseInt(edt_int_02.text.toString()))
val num3: Num = Num(Integer.parseInt(edt_int_03.text.toString()))

val result: Int = evalWithLogging(Sum(Sum(num1, num2), num3))
txt_cal_result.setText("When 블록 사용 : " + result.toString())
txt_cal_log.setText(log)
}
}

// if 연쇄를 사용해 계산
fun eval1(e: Expr): Int {
if (e is Num) {
val n = e as Num // 위에서 이미 is Num으로 인해 스마트 캐스트 되어 as Num하여 타입을 변경할 필요 없다
// 자바에서는 instanceof로 타입을 검사한 후 명시적으로 타입을 변경해줘야 하지만 코틀린에선 is로 타입을 검사 하면 스마트 캐스팅되어 해당 타입으로 사용이 가능하다
return n.value
}
if (e is Sum) {
return eval1(e.right) + eval1(e.left) // 변수 e에 대해 스마트 캐스트를 사용한다
}
throw IllegalArgumentException("Unknown expression")
}

// 리팩토링 1 - 값을 만들어내는 if 식
fun eval2(e: Expr): Int =
if (e is Num) {
e.value
} else if (e is Sum) {
eval2(e.right) + eval2(e.left)
} else {
throw IllegalArgumentException("Unknown expression")
}

// 리팩토링 2 - if 중첩 대신 when 사용하기
fun eval3(e: Expr): Int =
when (e) {
is Num -> e.value
is Sum -> eval3(e.right) + eval3(e.left)
else -> throw IllegalArgumentException("Unknown expression")
}

// 리팩토링 3 - 분기에 복잡한 동작이 들어가 있는 when 사용하기
fun evalWithLogging(e: Expr): Int =
when (e) {
is Num -> {
log += "num: ${e.value}\n"
e.value
}
is Sum -> {
val left = evalWithLogging(e.left)
val right = evalWithLogging(e.right)
log += "sum: $left + $right\n"
left + right
}
else -> throw IllegalArgumentException("Unknown expression")
}
}



3. 실행 결과


+ Recent posts