[kotlin] 개념

·2025년 4월 26일

just공부

목록 보기
20/41

1. Android Intro.

Android Core Component

  • 인스턴트화 가능한 4개의 컴포넌트 + 메시징 메커니즘
  • 진입점은 따로 정해져 있지 않음
  • 일반적으로 액티비티가 진입점 역할
  • 액티비티(Activity) : 화면 제어, 사용자 인터페이스 구성
  • 서비스(Service) : 백그라운드에서 계속 동작
  • 방송 수신자(BR, Broadcast Receiver) : 방송을 대기하고 수신
  • 컨텐트 제공자(CP, Content Provider) : 자신의 데이터를 외부로 공개
  • 인텐트(Intent) : 컴포넌트끼리 통신하기 위해 사용하는 메시징 메커니즘

코틀린 (Kotlin)

특징

  • Java와 상호 호환
    • 기존의 Java 기반의 프로젝트에 추가 가능
    • Kotlin에서 Java API 호출 가능
    • Java로 개발한 앱을 Kotlin으로 변환 가능
  • Java에 비해 간결한 구문
  • Null Safety
  • Coroutine을 사용한 비동기 프로그래밍

2. Kotlin 기초 01

Kotlin 개요

  • JetBrains에서 개발한 프로그래밍 언어
    • JVM 기반 언어 -> Java와 호환 (Java Bytecode로 변환)

특징

  • 간결한 프로그램 구문
  • Java와 호환
  • Null safety 지원
  • 비동기 프로그래밍 용이

Variable 01

  • 변수 선언 : val (or var) 변수명 [: 자료형] = 초깃값

  • val : value, 상수, 초기화 값으로 값 고정, 초기화 값 필수

  • var : variable, 변수 선언 후 필요에 따라 값 변경

  • 자료형을 생략했을 경우, 초기화 값의 자료형으로 결정

val myValue = 10 // Integer 형

val data01 : Int = 1234 // 전역변수 또는 클래스의 멤버변수는 반드시 초기화 수행 -> 전역변수: 초기화 필수, 멤버변수 : 초기화 선택
var data02 = 5

fun main() {
	val data01 : Int // Int :  기본 타입 모두 class
	var data03: String
	data01 = 4321
	data03 = "Som"
	println("Hello World!! $data01 $data03") // 문자열(" ") 내부에서 $변수명 사용하여 변수값 출력 가능
	
	// 수식을 넣으려면 ${변수 +1} 
}

Variable 02

초기화 지연

  • 필요에 따라 변수의 선언 이후에 초기화를 진행해야 할 경우 적용
  • lateinit var 변수 : 자료형
    • var로 선언한 변수의 초기화 지연
    • 기본 자료형에는 적용 불가
  • val 변수 [: 자료형] by lazy { }
    • val 변수의 최초 사용 시점에 by lazy 블록이 실행되며, 초기 작업 및 초기화 수행
lateinit var data01: String
val data02: Int by lazy {
	println("val init")
	1234
}
fun main(){
	data01 = "Mobile"
	println("data01: $data01 data02: $data02")
}

자료형(Data Type) 01

객체 자료형

  • 모든 자료형은 객체 (Kotlin Int == Java Integer)
  • 기본적으로 null 대입 불가
    • non-null type(NULL safety)
    • 변수 선언 시 ? 를 기록하여 null 대입 가능
    • var data01 : Int = null (x)
    • var data02 : Int? = null (O)

문자열 String

  • "일반문자열" 또는 """띄어쓰기/탭/줄바꿈이 그대로 표현"""

Any, Unit, Nothig 자료형

  • Any : 모든 객체의 최상위 타입 (Java Object)
  • Unit : 반환타입 없음으로 표시 (Java void와 유사)
  • Nothin : null 또는 Exception 반환 표시 (null의 경우 ? 사용)

자료형(Data Type) 02

var data01 : Int = 10 // 변수 선언
var data02 : Int? = null // null 대입 가능
var data03 : Any = "20" // Any를 사용하여 문자열 저장

fun main(){
	var data04 = data01.toFloat() // 현재 자료형을 Float으로 변환, 객체이므로 멤버함수 사용 가능
	println("$data01, ${data02}, $data03 $data04")
}

fun myFunction01() : Unit { // 반환타입 없음
	println("myFunction01")
}

fun myFunction02() : Nothing? { // null 반환 -> ? 사용
	return null
}

fun myFunction03 () : Nothing { // Exceptional throw
	throw Exception()
}

함수 (Function)

선언

fun 함수명 (매개변수명: 타입[, ... ] : 반환타입 {함수본체})

  • [반환타입]이 없을 경우 Unit 사용 또는 생략
  • 매개변수는 val/var 키워드 사용X
  • val 취급
fun myPower01(value: Int) : Int {
	return value * value*
}

fun myAdd(value: Int) : Unit {
	// value = 10
}

fun myDefault (value : Int = 1) : Int {
	return value * value*
}

fun mySelection (value01: Int, value02: Int) : Unit {
	print("add == ${value01 + value02}")
}

fun main(){
	println("${myDefault()}     ${myDefault(10)}")
	println("${mySelection(1, 2)}     ${mySelection(value02=1, value01=2)}")
}

배열(Array)과 컬렉션(Collection)

배열 Array

  • Size 고정
  • 배열도 class라서 .set() , .get() 사용 가능
  • [ ] 사용 권장
val myArray: Array<Int> = Array(3, {0})   // 3은 size, 0은 초기화값
  
fun main(){  
    println(" ${myArray[0]}, ${myArray.get(0)}")  
    myArray[1]  = 10  
    myArray.set(2, 20)  // set(위치, 값)
    println(" ${myArray[1]}, ${myArray.get(2)}")  
}

기본 자료형 별 배열 선언

  • ~of : 배열 생성 함수
  • Int 배열의 예
val int Array01 : Array<Int> = Array(3, {0})
val intArray02 = IntArray(3, {0})
val intArray03 = arrayOf<Int>(1, 2, 3)
val intArray04 = intArrayOf(4,5,6)

fun main(){
	println("array01 size: ${ intArray01.size} ")
}

기본 Collection : List, Set, Map

Collection : 저장 크기의 실행 중 변경이 가능

  • List : 순서 있는 컬렉션, 값 중복 허용, 일반적
  • Set : 순서 없는 컬렉션, 값 중복 불가
  • Map : key와 value의 집합으로 구성, key로 value 접근

불변타입과 가변타입(mutable~) 으로 구분

val myList01 : List<Int> = List(3, {0})
val myList02 : listOf(1, 2, 3)
val myMuList01 : MutableList<Int> = MutableList(3, {0})
val myMuList02 = mutableListOf<Int>(1, 2, 3)

fun main(){
	// myList01[0] = 10 // 오류 발생
	myMuList01[0] = 10
	myMuList02.add(4) // 새로운 항목 추가
	myMuList02.set(0, 10) // 기존 항목 변경
	println("${myMuList01}    ${myMuList02}")  // 실행 결과 : [10, 0, 0, 4]    [10, 2, 3]
}

조건문, 반복문

if, if-else

fun main(){
	val value = 10
	if(value > 10){
		println("Up")
	} else {
		println("Down")
	}
	val result : Boolean = if (value > 5) {
		true // 표현식  - 값 반환
	} else {
		false
	}
	println("result : $result ")
}

when

  • Java의 switch-case 문 역할, break는 없음
fun main() {
	val data : Any = 10
	val result = when (data) { // 표현식 사용 가능, 조건에 String도 사용 가능
		is Int -> { // is : datatype , 자료형 확인
			println("Integer!")
			true
		}
		is String -> false
		10 -> true
		in 5..10 -> true // 범위 확인 가능, 5 <= data <= 20
		else -> false
	}
	println("result : $result")
}

while

  • 반복 횟수가 정해지지 않은 경우 적합
fun main() {
	while (true) {
		print("Input a number  (0 for exit) : ")
		val inputData = readLine()?.toInt()
		if (inputData != 0) continue
		else break
	}
}

for

  • 범위 연산자 in 사용
fun main() {
	for (i in 1..10 print ("$i ")) // 1 <= i <= 10
	println()
	for (i in 1 until 10) print ("$i  ") // 1 <= i < 10
	println()
	for(i in 1..10 step 3) print ("$i   ") // 1부터 10까지 3씩 증가
	println()
	for (i in 10 downTo 2) print ("$i  ") // 10부터 2까지 감소 , 10 >= i  >= 2
}

in 연산자

  • list 등의 컬렉션 항목을 순차적으로 사용
int main() {
	val data = intArrayOf(1, 2, 3, 4, 5)

	for (value in data) print ("$value")
	println()
	for (index in data.indices) { // indices : collection의 index 집합
		println("index: $index value : ${data[index]}")
	}
	for ((index, value) in data.withIndex()) {
		println("[$index] : $value") // withIndex() : index와 value 함께 반환
	}
}

Kotlin 기초 02

람다 함수 (Lambda function)

in Kotlin

  • 이름 없는 익명함수로 표현되는 코드 조각
  • 변수 취급 가능 (변수에 대입, 매개변수로의 전달 등)
  • 기본 형식
    • {매개변수 ... -> 함수 Body } <= 함수 조각

람다함수의 사용

  • 함수명이 없으므로, 필요 시 변수에 대입한 후 변수명으로 함수 사용
  • 람다함수의 마지막 값은 return 값 취급
  • 따로 return 을 사용하지 않음
  • 매개변수가 없을 경우 생략 가능
val almbdaNoParam = { -> println("lambda")} // { println("lambda!")} 
lambdaNoParam()

val labdaWithParam = { num1: Int -> num1 + 10 }
// val lambdaWithParam : (Int) -> Int  = { num1: Int -> num1 + 10} //  변수 할당 시 람다 함수 타입 선언
val result = lambdaWithParam(10)
println(result)


// 매개변수가 있는 람다함수의 예
val lambdaFunc : (Int, String) -> Unit = { num: Int, str: String -> 
	println(num * num)
	println(str)
}

// it 키워드 사용
val lambdaPower : (Int) -> Int = {num : Int ->
	num * num // 반환값은 표현식 형태
}
println(lambdaPower(2))
val lambdaWithIt : (Int) -> Int = {
	it * it // 매개변수가 하나일 경우, 해당 매개변수 표현
}
println(lambdaWithIt(3))

고차 함수(Higher-order function) 01

  • 다른 함수를 매개변수로 전달받거나 반환할 수 있는 함수
    -> 함수형 프로그래밍(Functional programming) 기법
val nameFunc : () -> Unit = { // return 없고, 매개변수 X -> 생략가능
	println ("som!")
}
val subjectFunc: () -> Unit = { // 매개변수X -> 생략가능
	val subjectName = "Mobile softeware"
	println(subjectName)
}
fun higherOrderFunc(argFunc: ( ) -> Unit) { // 함수를 매개변수로 전달받음, 매개 X, return X 함수
	println("Dept: computer ")
	argFunc()
}
 // nameFunc, subjectFun 모두 해당 함수의 매개변수로 들어갈 수 있음
higherOrderFunc(nameFunc)
higherOrderFunc(subjectFunc)
  • 고차함수를 반환값으로 지정
    • 함수의 반환 타입을 고차 함수로 선언 가능
fun higherOrderFunc () : (String) -> Unit { // 매개변수로 String을 받음, 반환 타입 X
	return { grade ->
		println("Dept: Computer")
		println("Subject: Mobile Software")
		println("Grade : $grade" )
	} // 문자열 type인 grade를 전달 받아 화면 출력을 수행하는 람다 함수 
}

val returnedFunc = higherOrderFunc()
returnedFunc("A") // return { }  잘에 "A"가 오는 것.

Null 안정성 (Null-safety) 연산자 01

  • 객체가 null인 상태에서 사용할 때 발생하는 NullPointerException을 예방하기 위해 사용
    • null 인 객체를 사용하므로 실행할 경우 NullPointerException 발생
    • 일반적인 변수는 null 값을 가질 수 없도록 제한
  • [타입?] 연산자 : 변수 선언 시 null을 할당가능한 변수로 선언하고자 할 때 사용
  • [객체?.멤버] 연산자 : 객체가 null 상태일 경우 null 이라는 상수 값으로 대체
    • ? 연산자를 사용하여야만 null 할당 가능
    • ?. 사용하여 객체가 null 상태일 경우 null이라는 값으로 대체 가능

Null 안정성 (Null-safety) 연산자 02

[ 값 ?: 대체값 ] 연산자

  • 엘비스 연산자 -> null 이 아니면 왼쪽, null 이면 오른쪽 값
  • [값] 부분이 NullPointerException이 발생하지 않는 경우에는 [값] 사용, 발생할 경우 [대체값] 사용
var str: String? = "Mobile"
println(str?.length ?: 0)
str = null
println (str?.length ?: 0) 
// 결과 
6
0

[값 !!]연산자

  • [값] 부분이 null 일 경우 NullPointerException 발생
  • 개발자가 예외 상태를 확인할 필요 있음
  • 개발자가 인지하고 사용하고 싶을 때만 null 가능
var str: String? = null
println(str!!.length ?: 0) //NullPointerException 발생

Class

  • class 선언 시, Body가 없는 클래스도 선언 가능

생성자(Constructor)

  • 주생성자 : class 선언부에 선언
  • constructor 키워드 생략 가능
  • 생성자 선언이 없을 경우 매개변수가 없는 주생성자 생성
  • 주생성자 body는 init { }에 기술 : 생략 가능
class MyClass constructor(dept: String){
	var dept : String

	init {
		this.dept = dept
	}
}

class MyClass (dept: String) {  // constructor 키워드 생략
	var dept : String 

	init {
		this.dept = dept // init {  } 주 생성자 body => 가장 기본 생성자
	}
}

class MyClass {
	var dept : String // 멤버변수는 init  {  }가 있을 경우 초기화 생략 가능

	init {
		dept = "computer" // 주생성자 생략 -> 매개변수가 없는 주생성자 자동 생성
	}
}

fun main() {
	val myDept = MyClass("computer") // 객체 생성
}

주 생성자에서 매개변수 선언 시

  • val 또는 var 키워드 사용 시 멤버변수로 지정
class MyClass (dept: String)  // dept : 주생성자의 매개변수 -> 지역변수 취급
	init{
		println("$dept")
	}
	fun printDept(){
		println("dept: $dept") // dept는 소멸한 상태라 오류 발생
	}
}

class MyClass(val dept : String){ // val 을 사용하였으므로 dept는 멤버변수
	init{
		println("$dept")
	}
	fun printDept(){
		println("dept: $dept")
	}
}

보조 생성자

  • 주생성자 이외의 생성자
  • class body 부분에 constructor 키워드로 선언
class MyClass {
	constructor (dept :String) { // 주생성자 , 매개변수가 없는 주생성자와 하나의 매개변수를 갖는 보조생성자 존재
		println("${dept}")
	}
}
// 주생성자를 명시적으로 구현할 경우 반드시 보조생성자에서 주생성자 최종적으로 호출
// 보조 생성자의 개수와 상관없음

// 매개변수가 없는 주생성자 선언 포함
// 보조생성자 호출 후 주생성자를 호출하도록 구현 필요
class MyClass(){ // () 가 없으면 주생성자
	constructor(dept: String) : this(){ // 보조생성자, this() <- 주생성자 호출
		println("${dept}")
	}
}

클래스 상속(Inheritance)

  • 기존 클래스 멤버 상속
  • 상속 자격을 만들기 위해 open 선언한 경우에만 선언 가능
open class Superclass {
	init{
		println("Super class") // 먼저 항상 수행됨
	}
}

class Subclass : SuperClass() { // 상위클래스의 생성자 호출 필요
	init {
		println("Sub class")
	}
}

멤버의 재정의(Overriding)

  • open으로 선언한 멤버만 재정의 가능
    • 자식에서 수정 가능
  • 재정의하는 항목은 override 키워드 표시
  • 접근제어 지정
    • public/proteted/private
    • Internal : 같은 모듈 허용
    • final 과 유사
open class SuperClass{
	var memVar01 = 1
	open var meVar02 = 2
	fun memFunc01() {
		println("super01!")
	}
	open fun memFun02() {
		println("super02!")
	}
}
// 상위클래스의 open 키워드를 기록한 멤버만 재정의 가능
class SubClass  : SuperClass(){  // 상위클래스의 생성자 호출 필요요
	override var memVar01 =2 // 오류 발생
	override var memVar02 = 3 // 수정 가능!
	override fun memFunc01() { // open 키워드 없기 때문에 오버라이딩 불가
		println("sub01!")
	}
	override fun memFunc02(){
		println("sub02!")
	}
}

유용한 클래스

data 클래스

  • 상수값을 보관하는 용도로 사용하는 클래스
  • VO(Value Object) 또는 DTO(Data Transfer Object)
  • toString(), equals() 등 자동으로 구현됨
  • == 연산자로 값 비교 가능
data class Subject(val title: String, var credit: Int)

fun main(){
	val sub1 = Subject("mobile", 3)
	val sub2 = Subject("mobile", 3)

	println("${sub1}") // 결과 : Subject(title=mobile, credit=3)
	println("${sub1.equals(sub2)}") // 결과 : true ,
	//  java의 경우 서로 다른 객체이므로 false 반환, Kotlin의 data 클래스는 멤버변수의 값이 같으면 equals()가 true 반환
}

익명 클래스의 선언

  • class 또는 interface에서 상속받는 클래스의 선언 없이 임시 객체를 만들고자 할 때 사용
  • object 키워드 사용
open class SuperClass{
	init {
		println("Super class")
	}
}
interface SomeInterface {
	val dept : String
	fun getDept()
}

val obj1 = object : SuperClass(){ //SuperClass를 상속하면서 새로운 멤버변수와 멤버함수를 추가한 객체 생성
	var memValue = 10
	fun memFunc() {
		println("Object class!")
	}
}
val obj2 = object: SomeInterface { 
	override val dept : String = "computer"
	override fun getDept(){
		println("$dept") // interface에서 직접 필요한 객체 생성
	}
}

Companion object

  • 클래스의 모든 객체들이 함께 사용하는 멤버 정의
  • JAVA의 static과 유사
class MyClass {
	companion object {
		var coData = 10
		fun coFunc () {
			println("Companion!")
		}
	}
 } // MyClass로 생성한 모든 객체들이 공용
 // static 과 유사한 방식으로 사용
fun main() {
	println("${MyClass.coData}")
	println("${MyClass.coFunc()}")
}
profile
Whatever I want | Interested in DFIR, Security, Infra, Cloud

0개의 댓글