글쓴이 보관물: KDIDI

Kotlin 안드로이드 앱 프로그래밍 (10)

보조 생성자
보조 생성자는 클래스의 본문에 constructor 키워드로 선언하는 함수, 클래스 본문에 선언하므로 여러 개를 추가할 수 있습니다.
다음 코드에서는 매개변수를 다르게 구성한 보조 생성자 2개를 선언했습니다. 보조 생성자도 생성자 이므로 객체를 생성할 때 자동으로 호출됩니다. 그리고 보조 생성자는 클래스 본문에 선언하므로 생성자 본문을 중괄호 {}로 묶어서 객체 생성과 동시에 실행할 영역을 지정할 수 있습니다.

보조 생성자 선언

class User{
   constructor(name: String){
      println("constructor(name: String) call...")
   }
   constructor(name: String, count: Int){
      println("constructor(name: String, count: Int) call...")
   }
}
fun main(){
   val user1 = User("kkang")
   val user2 = User("kkang", 10)
}

>> 실행결과
constructor(name: String) call...
constructor(name: String, count: Int) call...

보조 생성자에 주 생성자 연결
코틀린의 생성자는 주 생성자와 보조 생성자로 나뉩니다. 클래스를 선언할 때 둘 중 하나만 선언하면 문제가 없지만, 만약 주 생성자와 보조 생성자를 모두 선언한다면 반드시 생성자끼리 연결해 주어야 합니다.

주 생성자와 보조 생성자 선언 시 오류

class User(name: String){
   constructor(name: String, count: Int){ // 오류
---
   }
}

클래스에 주 생성자와 보조 생성자를 모두 선언하였습니다. 그런데 위의 코드는 오류가 발생합니다. 주 생성자가 없다면 보조 생성자를 선언하는 데 문제가 없지만 주 생성자가 있으므로 보조 생성자에서 주 생성자를 호출해 주어야 합니다.
보조 생성자는 객체를 생성할 때 호출되며, 이때 클래스 내에 주 생성자가 있다면 this() 구문을 이용해 주 생성자를 호출해야 합니다.

보조 생성자에서 주 생성자 호출

class User(name: String){
   constructor(name: String, count: Int): this(name){ // 성공
---
   }
}
fun main(){
   val user = User("kkang", 10)
}

보조 생성자 선언부에 this(name)만 추가한 코드입니다. 이렇게 하면 보조 생성자로 객체를 생성할 때 주 생성자가 함꼐 호출됩니다.
만약 주 생성자가 있는 상태에서 보조 생성자를 여러 개 선언한다면 보조 생성자에서 this() 로 다른 보조 생성자를 호출할 수도 있습니다. 그런데 이때에도 보조 생성자로 객체를 생성한다면 어떤 식으로든 주 생성자가 호출되게 해야 합니다.

보조 생성자가 여럿일 때 생성자 연결

class User(name: String){
   constructor(name: String, count: Int): this(name){
---
   }
   constructor(name: String, count: Int, email: String): this(name, count) {
---
   }
}
fun main(){
   val user = User("kkang", 10, "a@a.com")
}

같은 모양으로 묶음

클래스를 재사용하는 상속 – 상속과 생성자
클래스를 선언할 때 다른 클래스를 참조해서 선언하는 것을 상속 이라고 합니다. 코틀린에서 어떤 클래스를 상속받으려면 선언부에 콜론: 과 함께 상속받을 클래스 이름을 입력합니다. 이렇게 하면 기존 클래스를 재 사용할수 있습니다.

클래스 상속 형식

open class Super{ // 상속할 수 있게 open 키워드 이용
}
class Sub: Super(){ // Super를 상속받아 Sub 클래스 선언

상속 관계에서 상속 대상이 되는 클래스를 상위 클래스라고 하고, 상속받는 클래스를 하위 클래스라고 합니다. 코틀린의 클래스는 기본적으로 다른 클래스가 상속할 수 없습니다. 다시 말해 class Super{} 처럼 클래스를 선언하면 다른 클래스에서 Super 클래스를 상속할 수 없습니다. 만약 다른 클래스에서 상속할 수 있게 선언하려면 open 키워드를 사용해야합니다. 즉 open class Super {} 라고 선언하면 Super 클래스의 상속을 허용합니다.
하위 클래스를 선언할 때는 이름 뒤에 콜론(:)을 입력하고 상속받을 상위 클래스 이름을 입력합니다. class Sub: Super() {} 코드는 Super 클래스를 상속받아 Sub 클래스를 선언한 구문입니다.
상위 클래스를 상속받은 하위 클래스의 생성자에서는 상위 클래스의 생성자를 호출해야 합니다. class Sub: Super() {}코드에서 Super()는 Super 클래스를 상속받으면서 이 클래스의 매개변수가 없는 생성자를 호출합니다. 만약 매개변수가 있는 상위 클래스의 생성자를 호출할 때는 다음처럼 매개변수 구성에 맞게 인자를 전달해야 합니다.

매개변수가 있는 상위 클래스의 생성자 호출

open class Super(name: String){
}
class Sub(name: String): Super(name){
}

상위 클래스의 생성자 호출문을 꼭 클래스 선언부에 작성할 필요는 없습니다. 만약 하위 클래스에 보조 생성자만 있다면 상위 클래스의 생성자를 다음처럼 호출할 수 있습니다.

하위 클래스에 보조 생성자만 있는 경우 상위 클래스의 생성자 호출

open class Super(name: String){
}
class Sub: Super{
   constructor(name: String): super(name){
   }
}

Kotlin 안드로이드 앱 프로그래밍 (9)

주 생성자
코틀린에서의 클래스는 생성자를 주 생성자와 보조 생성자로 구분합니다. 한 클래스 안에 주 생성자만 선언할 수도 있고 보조 생성자만 선언할 수도 있습니다. 물론 둘 다 선언 가능합니다.
주 생성자는 constructor 키워드로 클래스 선언부에 선언합니다. 주 생성자 선언은 필수는 아니며 한 클래스에 하나만 가능합니다.

주 생성자 선언

class User constructor() {
}

주 생성자를 선언할 때 constructor 키워드는 생략할 수 있습니다.

constructor 키워드 생략 예

class User() {
}

만약 개발자가 클래스의 주 생성자를 선언하지 않으면 컴파일러가 매개변수가 없는 주 생성자를 자동으로 추가합니다.

매개변수가 없는 주 생성자 자동 선언

class User() {
}

주 생성자의 매개변수
주 생성자를 선언할 때 필요에 따라 매개변수를 선언할 수도 있습니다.

주 생성자의 매개변수

class User(name: String, count: Int){
}

앞의 코드는 User 클래스를 선언하면서 주 생성자에 매개변수를 2개 선언하였습니다. 그러면 객체를 생성할 때 매개변수의 타입과 개수에 맞는 인자를 전달해야 합니다.

매개변수가 있는 생성자 호출

val user = User("kkang", 10)

주 생성자의 본문 – init 영역
주 생성자를 이용해 객체를 생성할 때 특정한 로직을 수행할 수 있습니다. 그런데 주 생성자의 실행 영역인 본문을 다음처럼 추가하면 오류가 발생합니다.

주 생성자 중괄호 때문에 오류 발생

class User(name: String, count: Int){
     // 주 생성자 본문
}{   // 오류
     // 클래스 본문
}

보통 클래스나 함수의 본문(실행 영역)은 중괄호 {}로 감싸지만 주 생성자에는 {}를 추가할 수 없습니다. 왜냐하면 주 생성자는 클래스 선언부에 있기 때문입니다. 이럴 때 init 키워드를 이용해 주 생성자의 본문을 구현할 수 있습니다.
코틀린의 클래스 안에서 init 키워드로 지정한 영역은 객체를 생성할 때 자동으로 실행됩니다. 클래스에서 init 영역은 꼭 선언할 필요는 없으므로 주 생성자의 본문을 구현하고 싶을 때 사용합니다.
init 영역은 주 생성자뿐만 아니라 잠시 후에 다루는 보조 생성자로 객체를 생성할 때도 실행됩니다. 하지만 보조 생성자는 클래스 안에 선언하므로 {}를 이용해 본문을 지정할 수 있습니다. 따라서 init 영역은 일반적으로 주 생성자의 본문을 구현하는 용도로 사용합니다.

init 키워드로 주 생성자의 본문 지정

class User(name: String, count: Int){
   init {
      println("i am init...")
   }
}
fun main() {
   val user = User("kkang", 10)
}

>> 실행결과
i am init...

User라는 클래스에 주 생성자를 선언하고 클래스 본문에 init 영역을 두어 주 생성자의 본문을 작성하였습니다. 이렇게 하면 main() 함수에서 User 클래스의 객체를 생성할 때 init 영역에 작성한 코드가 자동으로 실행됩니다.

생성자의 매개변수를 클래스의 멤버 변수로 선언하는 방법
생성자의 매개변수는 기본적으로 생성자에서만 사용할 수 있는 지역 변수입니다.

생성자의 매개변수를 init 영역에서 사용하는 예

class User(name: String, count: Int){
   init{
      println("name : $name, count : $count") // 성공
   }
   fun someFun(){
      println("name : $name, count : $count") // 오류
   }
}

위 코드에서는 주 생성자에 name, count 매개변수를 선언하였습니다. 그리고 이 변수를 init 영역과 someFun() 이라는 함수에서 사용하려고 합니다. 생성자를 호출할 때 init 영역이 실행되므로 이곳에서 생성자의 매개변수에 접근할 수 있습니다.
하지만 생성자의 매개변수는 지역 변수이므로 다른 함수에서는 사용할 수 없습니다. 만약 생성자의 매개변수를 클래스의 멤버 변수처럼 다른 함수에서 사용해야 한다면 다음처럼 작성해야 합니다.

생성자의 매개변수를 다른 함수에서 사용하는 예

class User(name: String, count: Int){
   // 클래스 멤버 변수 선언
   var name: String
   var count: Int
   init{
      // 클래스 멤버 변수에 생성자 매개변숫값 대입
      this.name = name
      this.count = count
}
   fun someFun(){
      println("name : $name, count : $count") // 성공
   }
}
fun main(){
   val user = User("kkang", 10)
   user.someFun()
}

>> 실행결과
name : kkang, count : 10

클래스의 멤버 함수 someFun()에서 생성자의 매개변수를 이용하고자 클래스의 멤버 변수를 선언하고 주 생성자의 본문인 init 영역에서 매개변숫값을 클래스의 멤버 변수에 대입하였습니다.
그런데 이 방법 말고도 생성자의 매개변수를 클래스의 멤버 변수로 선언하는 방법이 있습니다. 이렇게 하면 코드를 조금 더 간단하게 작성할 수 있습니다. 주 생성자의 매개변수는 생성자 안에서만 사용할 수 있는 지역 변수지만 매개변수를 var나 val 키워드로 선언하면 클래스의 멤버 변수가 됩니다.

생성자의 매개변수를 클래스의 멤버 변수로 선언하는 방법

class User(val name: String, val count: Int){
   fun someFun(){
      println("name : $name, count : $count") // 성공
   }
}
fun main(){
   val user = User("kkang", 10)
   user.someFun()
}

>> 실행결과
name : kkang, count : 10

원래 함수는 매개변수를 선언할 때 var나 val 키워드를 추가할 수 없습니다. 그런데 주 생성자에서만 유일하게 var나 val 키워드로 매개변수를 선언할 수 있으며 이렇게 하면 클래스의 멤버 변수가 됩니다. 따라서 위 코드에서 생성자의 매개변수 name과 count를 someFun()이라는 멤버 함수에서 사용할 수 있습니다.

Kotlin 안드로이드 앱 프로그래밍 (8)

For과 While
코틀린에서도 반복문 for과 while을 지원합니다. for 문은 제어 변숫값을 증감하면서 특정 조건이 참일 때까지 구문을 반복해서 실행합니다. 이때 for 문의 조건에는 주로 범위 연산자인 in을 사용합니다.

for-in 반복문

fun main(){
   var sum: Int = 0
   for (i in 1..10){
      sum += i
   }
   println(sum)
}
>> 실행결과
55 (1부터 10까지 순서대로 합함)

위 소스는 for 문을 이용해 1부터 10까지 더하기를 수행합니다. for 문의 소괄호 안에 있는 i in 1..10 코드는 1부터 10까지 1씩 증가하면서 for 문의 실행 영역을 반복하라는 의미입니다. 따라서 총 10번 반복해서 실행합니다.
for 문의 조건은 이와 같이 단순히 1씩 증가 또는 감소하게 만들 수도 있고 2씩 증가하게 만드는 등 다양하게 작성할 수 있습니다.

for (i in 1..10) {…} -> 1부터 10까지 1씩 증가
for (i in 1 until 10) {…} -> 1부터 9까지 1씩 증가(10은 미포함)
for (i in 2..10 step 2) {…} -> 2부터 10까지 2씩 증가
for (i in 10 downTo 1) {…} -> 10부터 1까지 1씩 감소

증감 조건을 숫자로 명시하지 않고 컬렉션 타입의 데이터 개수만큼 반복하게 할 수도 있습니다.

반복 조건에 컬렉션 타입 활용

fun main(){
   var data = arrayOf<Int>(10, 20, 30)
   for (i in data.indices){
      print(data[i])
      if(i !== data.size - 1) print(",")
   }
}
>> 실행결과
10,20,30

배열의 크기만큼 for 문을 반복하게 작성한 소스입니다. indices는 컬렉션 타입의 인덱스값을 의미하므로 for 문을 반복하면서 0,1,2 값을 i에 대입합니다. 만약 for 문을 반복하면서 인덱스와 실제 데이터를 함께 가져오려면 withIndex() 함수를 이용합니다.

인덱스와 데이터를 가져오는 withIndex() 함수

fun main(){
   var data = arrayOf<Int>(10,20,30)
   for ((index, value) in data.withIndex()){
      print(value)
      if (index !== data.size-1) print(",")
   }
}
>> 실행결과
10,20,30

for 문 외에 while 문을 이용해 반복문을 작성할 수도 있습니다. while 문은 조건이 참이면 중괄호 {}로 지정한 영역을 반복해서 실행합니다.

while 반복문

fun main(args: Array<String>){
   var x = 0
   var sum1 = 0
   while (x < 10) {
      sum += ++x
   }
   println(sum1)
}
>> 실행결과
55

클래스와 생성자
클래스 선언
코틀린에서 클래스는 class 키워드로 선언합니다. 다음 코드에서 class User 부분이 클래스의 선언부이며 중괄호 {} 영역이 본문입니다. 만약 클래스의 본문에 입력하는 내용이 없다면 {}를 생략할 수 있습니다.

[ 클래스 선언 – class User { } ]

클래스의 멤버는 생성자, 변수, 함수, 클래스로 구성됩니다. 이 중에서 코틀린의 생성자는 constructor 라는 키워드로 선언하는 함수입니다. 그리고 클래스 안에 다른 클래스를 선언할수도 있습니다.

클래스의 멤버

class User {
   var name = "kkang"
   constructor(name: String){
      this.name = name
   }
   fun someFun(){
      println("name : $name")
   }
   class SomeClass {}
}

클래스는 객체를 생성해 사용하며 객체로 클래스의 멤버에 접근합니다. 그런데 코틀린에서는 객체를 생성할 때 new 키워드를 사용하지 않습니다.

객체 생성과 멤버 접근
val user = User("kim")
user.someFun()

User(“kim”)이 객체를 생성하는 구문이며 클래스 이름과 같은 함수로 객체를 생성합니다.
객체를 생성할 때 생성자가 자동으로 호출되므로 소괄호 안에 전달한 인자는 클래스에 선언된 생성자의 매개변수와 들어맞아야 합니다. 앞에서 작성한 User 클래스의 생성자는 constructor(name: String)이므로 문자열 데이터를 전달받는 매개변수가 있습니다. 따라서 객체를 전달할 때 User(“kim”)처럼 문자열 데이터를 전달해 주어야 합니다.

Kotlin 안드로이드 앱 프로그래밍 (7)

조건문과 반복문
조건문 if-else와 표현식

if-else 문

fun main() {
   var data = 10
   if (data > 0) {
      println("data > 0") */ data 0 보다 크면 */
   } else {
      println("data <= 0") */ data 0 보다 작거나 같을때 */
   }
}
> 실행결과
data > 0

if 문에 명시한 조건을 만족하면 if 부분을 실행, 그렇지 않으면 else 부분을 실행합니다. 또한 else if 문을 이용해서 아래와 같은 조건을 통해서 여러 개 나열할 수도 있습니다.

조건을 여러 개 나열한 예

fun main() {
   var data = 10
   if (data > 10) {
      println("data > 10") */ data 10 보다 클때 10 제외 */
   } else if (data > 0 && data <= 10") */ data 0 보다 크거나 10 보다 같거나 작을 때 */
      println("data > 0 && data <= 10")
   } else {
      println("data <= 0") */ data 0 또는 0 보다 작을 때 */
   }
}
> 실행 결과
data > 0 && data <= 10

위의 if-else문 else if 은 대부분의 프로그래밍 언어에서 제공하는 조건문과 차이가 없습니다. 그런데 코틀린에서는 if-else문은 표현식으로도 사용가능합니다. 표현식이란 결괏값을 반환하는 계산식을 말합니다. if-else문은 단순히 조건에 맞는 영역을 실행하는 용도로 사용해도 되지만, 결괏값을 반환하는 표현식으로도 사용가능합니다.

표현식으로 사용한 예

fun main() {
   var data = 10
   val result = if (data > 0) {
      println("data > 0")
      true
} else {
      println("data <= 0")
      false
   }
   println(result)
}
> 실행결과
data > 0
true

위의 소스를 보면 var result = if () {} else {} 형태로 작성하였습니다. 즉 if-else문의 조건에 맞는 코드를 실행하는 동작 외에 그 결괏값을 result 라는 변수에 대입하고 있는 if-else문 표현식 입니다.
if-else문을 표현식으로 사용하려면 else를 생략할 수 없습니다. if만 사용하거나 if-else를 if로 사용할 수 없다는 뜻입니다. 항상 else 구문이 있어야 하며 if-else 혹은 if-else if-else 형태로 사용하여야 합니다.
그리고 if-else 표현식이 반환하는 결괏값은 각 영역의 마지막 줄에 해당합니다. 즉 if-else 부분을 여러 줄로 작성했더라도 마지막 줄의 실행 결과만 반환합니다.

조건문 when
코틀린은 if-else문 말고도 when이라는 조건문을 작성할 수도 있습니다.

when 문 사용 예시

fun main() {
   var data = 10
   when (data) {
      10 -> println("data is 10")
      20 -> println("data is 20")
      else -> {
         println("data is not valid data")
      }
   }
}
> 실행 결과
data is 10

when 키워드 다음의 소괄호 () 안에 넣은 데이터가 조건이 되고 이 값에 따라 각 구문을 실행 합니다. 10 -> println() 코드는 when의 조건으로 지정한 데이터가 10 이면 -> 오른쪽에 있는 구문을 실행합니다.
when 문의 조건으로 정수가 아닌 다른 타입의 데이터를 지정할 수도 있습니다.

문자열 타입을 조건으로 사용

fun main() {
   var data = "hello"
   when (data) {
      "hello" -> println("data is hello")
      "world" -> println("data is world")
      else -> {
         println("data is not valid data")
      }
   }
}
> 실행 결과
data is hello

위 코드는 when 문에 지정한 데이터가 문자열이며 문자열 값에 따른 조건을 명시했습니다. 이처럼 when 문에서는 조건을 데이터 타입, 범위 등으로 다양하게 명시할 수 있습니다.

다양한 유형의 조건 제시

fun main() {
   var data: Any = 10
   when (data) {
      is String -> println("data is String") // data가 문자열 타입
      20, 30 -> println("data is 20 or 30") // data가 20 또는 30
      in 1..10 -> println("data is 1..10") // data가 1-10의 값
      else -> println("data is not valid")
   }
}
> 실행 결과
data is 1..10

위 소스에서 is는 타입을 확인하는 연산자이며 in은 범위 지정 연산자입니다. is String은 데이터가 String 타입이면 참이고, in 1..10은 데이터가 1부터 10까지 범위이면 참입니다.
또한 when 문을 이용하면서 데이터를 명시하지 않고 조건만 명시할 수도 있습니다.

조건만 명시한 예

fun main() {
   var data = 10
   when {
      data <= 0 -> println("data is <= 0")
      data > 100 -> println("data is > 100")
      else -> println("data is valid")
   }
}
> 실행 결과
data is valid

when은 if 문과 마찬가지로 표현식으로도 사용할 수 있습니다. when 문을 표현식으로 사용할 때는 else 문을 생략할 수 없습니다.

when 문을 표현식으로 사용

fun main() {
   var data = 10
   var result = when {
      data <= 0 -> "data is <= 0"
      data > 100 -> "data is > 100"
      else -> "data is valid"
   }
   println(result)
}
> 실행 결과
data is valid

Kotlin 안드로이드 앱 프로그래밍 (6)

함수의 매개변수에는 기본값(default value)을 선언할 수 있습니다. 만약 어떤 매개변수에 기본값을 선언했다면 호출할 때 인자를 전달하지 않아도 되며 이때 선언문에 명시한 기본값이 적용됩니다.

* 기본값 활용

fun main(){
    fun some(data1: Int, data2: Int = 10): Int{
        return data1 * data2
}
    println(some(10))
    println(some(10, 20))
}

>> 실행결과
100
200

어떤 함수의 매개변수가 여러 개면 호출할 때 전달한 인자를 순서대로 할당합니다. 즉, 첫 번째 인자를 첫 번째 매개변수에 할당합니다. 그런데 호출할 때 매개변수명을 지정하면 매개변숫값의 순서를 바꿔도 됩니다.

* 매개변수명 생략 - 매개변수 순서대로 할당

fun some(data1: Int, data2: Int): Int {
    return data1 * data2
}
println(some(10, 20))

위 소스처럼 some() 이라는 함수에 매개변수를 2개 선언하고 some(10, 20)으로 함수를 호출하면 data1에 10, data2에 20을 대입합니다. 그런데 오른쪽처럼 매개변수명을 지정해서 호추할 수 있습니다.

* 매개변수명을 지정하여 호출

some(data2 = 20, data1 = 10)

매개변수명을 지정하여 호출하는 것을 명명된 매개변수라고 합니다. 이렇게 하면 함수 선언문의 매개변수 순서에 맞춰 호출하지 않아도 됩니다.

컬렉션 타입
여러개의 데이터를 표현하는 방법이며, Array List Set Map 이 있습니다.
Array – 배열 표현
코틀린의 배열은 Array 클래스로 표현합니다. Array 클래스의 생성자에서 첫 번째 매개변수 배열의 크기이며 두 번째 매개변수는 초깃값을 지정하는 함수 입니다. 배열의 타입은 제네릭으로 표현합니다. Array<Int>로 선언하면 정수 배열을 의미하며 Array<String>으로 선언하면 문자열 배열을 의미합니다.

* Array 클래스의 생성자

<init>(size: Int, init: (Int) -> T)

오른쪽 코드는 Array() 생성자의 첫 번째 인자가 3이고 두번 째 인자는 0을 반환하는 람다 함수이므로 0으로 초기화한 데이터를 3개 나열한 정수형 배열을 선언합니다.

* 배열 선언 예

val data1: Array<Int> = Array(3, {0})

배열의 데이터에 접근할 때는 대괄호([])를 이용해도 되고 set()이나 get() 함수를 이용할 수도 있습니다.

* 배열의 데이터에 접근하는 예

fun main(){
    val data1: Array<Int> = Array(3, {0})
    data[0] = 10
    data[1] = 20
    data.set(2, 30)

    println(
        """
    array size : ${data1.size}
    array data : ${data1[0]}, ${data1[1]}, ${data1.get(2)}
    """
    )
}

>> 실행결과
array size : 3
array data : 10, 20, 30

기초 타입의 배열
기초 타입이라면 Array 클래스를 사용하지 않고 각 기초 타입의 배열을 나타내는 클래스를 이용할 수도 있습니다. 즉 BooleanArray, ByteArray, CharArray, DoubleArray, FloatArray, IntArray, LongArray, ShortArray 클래스를 이용할 수도 있습니다.

* 기초 타입 배열 선언

val data1: IntArray = IntArray(3, {0})
val data2: BooleanArray = BooleanArray(3, {false})

또한 arrayof() 라는 함수를 이용하면 배열을 선언할 때 값을 할당할 수도 있습니다.

* 배열 선언과 동시에 값 할당

fun main(){
    val data1 = arrayOf<Int>(10, 20, 30)
    println(
         """
    array size : ${data1.size}
    array data : ${data1[0]}, ${data1[1]}, ${data1.get(2)}
    """
    )
}

>> 실행결과
array size : 3
array data : 10, 20, 30

arrayOf() 함수도 기초 타입을 대상으로 하는 booleanArrayOf(), byteArrayOf(), charArrayOf(), doubleArrayOf(), floatArrayOf(), intArrayOf(), longArrayOf(), shortArrayOf() 함수를 제공합니다.

* 기초 타입 arrayOf() 함수

val data1 = intArrayOf(10, 20, 30)
val data2 = booleanArrayOf(true, false, true)

List Set Map
Collection 인터페이스 타입으로 표현한 클래스이며 통틀어서 컬렉션 타입 클래스라고 합니다.
List : 순서가 있는 데이터 집합으로 데이터 중복을 허용
Set : 순서가 없으며 데이터의 중복을 허용하지 않음
Map : 키와 값으로 이루어진 데이터 집합으로 순서가 없으며 키의 중복은 허용하지 않음
Collection 타입의 클래스는 가변 클래스와 불변 클래스로 나뉩니다.
불변 클래스 : 초기에 데이터를 넣으면 이제 변경 불가
가변 클래스 : 초기에 데이터를 넣은 후에도 추가하거나 변경 가능
List를 예로 들면 코틀린에서는 가변과 불변이라는 2가지 타입의 클래스를 제공합니다. List는 불변 타입이므로 size(), get() 함수만 제공하고 데이터를 추가하거나 변경하는 add(), set() 함수는 제공하지 않습니다. 그런데 MutableList는 가변 타입이므로 size(), get() 함수 이외에 add(), set()함수를 이용할 수 있습니다. 이는 Set, Map 도 마찬가지 입니다. Set, Map 은 불변 타입이며 MutableSet, MutableMap은 가변 타입입니다.

* 가변 타입과 불변 타입성 * 

<구분> <타입>        <함수>               <특징>
List   List          listOf()            불변
       MutableList   mutableListOf()     가변
Set    Set           setOf()             불변
       MutableSet    mutableSetOf()      가변
Map    Map           mapOf()             불변
       MutableMap    mutableMapOf()      가변

다음 소스는 listOf() 함수로 List 객체를 만들며 매개변수에 초깃값을 대입합니다. List 객체의 데이터는 배열처럼 대괄호를 이용해 얻을 수도 있지만 get() 함수를 사용해도 됩니다.

* 리스트 사용 예

fun main(){
    var list = listOf<Int>(10, 20, 30)
    println(
    """
    list size : ${list.size}
    list data : ${list[0]}, ${list.get(1)}, ${list.get(2)}
    """
    )
}

>> 실행결과
list size : 3
list data : 10, 20, 30

MutableList는 mutableListOf() 함수로 만들며 데이터를 추가하거나 변경할 때는 add(), set() 함수를 이용할 수 있습니다.

* 가변 리스트 사용 예

fun main() {
    var mutableList = mutableListOf<Int>(10, 20, 30)
    mutableList.add(3, 40)
    mutableList.set(0, 50)
    println(
    """
    list size : ${mutableList.size}
    list data : ${mutableList[0]}, ${mutableList.get(1)},
                ${mutableList.get(2)}, ${mutableList.get(3)}
    """
   )
}

>> 실행결과
list size : 4
list data : 50, 20, 30, 40

Map 객체는 키와 값으로 이루어진 데이터의 집합입니다. Map 객체의 키와 값은 Pair 객체를 이용할 수도 있고 ‘키 to 값’ 형태로 이용할 수도 있습니다.

* 집합 사용 예

fun main() {
    var map = mapOf<String, String>(Pair("one", "hello"), "two" to "world")
    println(
    """
    map size : ${map.size}
    map data : ${map.get("one")}, ${map.get("two")}
    """
    )
}

>> 실행결과
map size : 2
map data : hello, world

위 코드에서 mapOf() 함수를 이용해 Map 객체를 만들었습니다. 이때 <String, String> 제네릭 타입을 지정하였고 따라서 Map 객체에 대입되는 데이터의 키와 값은 모두 String 타입이 됩니다. Pair(“one”, “hello”)처럼 키값을 Pair 객체로 표현해서 Map에 대입할 수도 있고, “two” to “world” 처럼 Map 객체에 대입할 수도 있습니다.

Kotlin 안드로이드 앱 프로그래밍 (5)

Char, Str (문자와 문자열)
Char는 문자를 표현하는 타입입니다. 코틀린 코드에서 Char 타입의 데이터는 문자를 작은따옴표 (‘ex) Char = ‘a’ ‘) 로 감싸서 표현합니다. 단 Number 타입으로는 표현할 수 없습니다.

* 문자 표현

val a: Char = 'a'
if (a==1) { // 오류 발생
}

Str는 문자열을 표현하는 타입입니다. String 타입의 데이터는 문자열을 큰 따옴표(“ex) “a” “)나 삼중 따옴표 (“””) 로 감싸서 표현합니다. 큰따옴표로 표현한 문자열에서 Enter나 Tab에 의한 줄 바꿈이나 들여쓰기 등을 그대로 유지하려면 역슬래시로 시작하는 이스케이프 시퀀스를 입력해야 합니다. 그러나 삼중 따옴표로 표현할 때는 키보드로 입력한 줄 바꿈이나 들여쓰기 등이 데이터에 그대로 반영됩니다. 다음 코드와 실행 결과를 참고해보면 됩니다.

* 문자열 표현

fun main(){
    val str1 = "Hi \n Hi"
    val str2 = """
        Hi
        Hi
    """
    println("str1 : $str1")
    println("str2 : $str2")
}
---------------------------------[실행결과]
str1 : Hi (\n 반영됨 줄바꿈)
 Hi
Str2 : (""" 키보드로 입력한 줄내용 반영)
       Hi
       Hi

안드로이드 스튜디오에서 삼중 따옴표를 사용하면 닫는 따옴표 뒤에 .trimIndent() 함수가 자동으로 추가됩니다. 이 함수는 문자열 앞에 공백을 없애 줍니다.
Str 타입의 데이터에 변숫값이나 어떤 연산식의 결괏값을 포함해야 할 때는 $ 기호를 이용합니다. 이를 문자열 템플릿 이라고 합니다.

* 문자열 템플릿

fun main(){
    fun sum(no: Int):Int{
      var sum = 0
    for (i in 1..no){
          sum += i
    }
    return sum
}

val name: String = "KDIDI"
println("name : $name, sum : ${sum(10)}, plus : ${10+20}")
-----------------------------------------------------------[실행결과]
name : KDIDI, sum : 55(1부터 10까지의 sum+=i에 넣은 값), plus : 30($10+20 결과)

Any – 모든 타입
Any는 코틀린에서 최상위 클래스입니다. 모든 코틀린의 클래스는 Any의 하위 클래스 입니다. 따라서 Any 타입으로 선언한 변수에는 모든 타입의 데이터를 할당할 수 있습니다.
(최상위 클래스)
모든 타입의 데이터를 할당할 수 있음

Any 타입 사용 예

val data1: Any = 10   -> int형
val data2: Any = "Hi" -> Str형

class User
val data3: Any = User() -> Class형

Unit – 반환문 없는 함수
Unit은 다른 타입과 다르게 데이터의 형식이 아닌 특수한 상황을 표현하려는 목적으로 사용합니다.
Unit 타입으로 선언한 변수에는 Unit 객체만 대입할 수 있습니다. 따라서 Unit 타입으로 변수를 선언할 수는 있지만 의미가 없습니다. 이런 Unit 타입은 주로 함수의 변환 타입으로 사용합니다. 함수에서 반환문이 없음을 명시적으로 나타날 때 Unit 타입을 사용합니다.

* Unit 타입 사용

val data1: Unit = Unit
* Unit 타입 사용 (반환문이 없는 함수)

fun some() : Unit{
    println(10+20)
}

함수를 선언할 때 반환 타입을 생략하면 자동으로 Unit이 적용됩니다. 즉, 오른쪽 소스는 위의 소스와 같습니다.

* 반환 타입을 생략한 예

fun some(){
    println(10+20)
}
--자동으로 Unit 적용됨--

Nothing – null이나 예외를 반환하는 함수
Nothing도 Unit과 마찬가지로 의미 있는 데이터가 아니라 특수한 상황을 표현합니다. Nothing 으로 선언한 변수에는 null만 대입할 수 있습니다. 즉, Nothing 으로 선언한 변수는 데이터로서는 의미가 없습니다.

* Nothing 사용

val data1: Nothing? = null

Nothing은 주로 함수의 반환 타입에 사용합니다. 어떤 함수의 반환 타입이 Nothing이면 반환은 하지만 의미 있는 값은 아니라는 의미 입니다. 항상 null만 반환하는 함수라든가 예외를 던지는 함수의 반환 타입을 Nothing 으로 선언합니다. (의미있는 값은 아니다.)

null 반환 함수와 예외를 던지는 함수

fun some1(): Nothing?{
    return null
}
fun some2(): Nothing{
    throw Exception()
}

널 허용과 불허용
코틀린의 모든 타입은 객체이므로 변수에 null을 대입할 수 있습니다. null은 값이 할당되지 않은 상황을 의미합니다. 코틀린에서는 변수를 선언할 때 null을 대입할 수 있는 변수인지, null을 대입할 수 없는 변수인지 명확하게 구분해서 선언해야 합니다.
(null 허용 nullable) (null 불허용 not null)
이러한 구분은 변수를 선언할 때 타입 뒤에 물음표(?)로 표시합니다. 타입 뒤에 물음표를 추가 하면 널 허용으로 선언하지만 반대로 물음표를 추가하지 않으면 불허용으로 선언합니다.

널 허용과 불허용

val data1: Int = 10
data1 = null // 오류!

val data2: Int? = 10
data2 = null // 성공!
-----------------------
? 유무는 있을때는 널 허용으로 선언
없을때는 널 불허용으로 선언 따라서 data1은 오류를 출력하게 되고
data2는 null을 대입하여도 정상적으로 출력하게 된다.

함수 선언하기
코틀린에서 함수를 선언하는 방법은 fun 이라는 키워드를 이용합니다.

* 함수 선언 방식

fun 함수명(매개변수명: 타입): 반환 타입 {...}

함수에는 반환 타입을 선언할 수 있으며 생략하면 자동으로 Unit 타입이 적용됩니다.

* 반환 타입이 있는 함수 선언

fun some(data1: Int): Int {
    return data1 * 10
}

함수의 매개변수에는 var나 val 키워드를 사용할 수 없습니다. val이 자동으로 적용되며 함수 안에서 매개변숫값을 변경할 수 없습니다.

* 매개변숫값 변경 오류

fun some(data1: Int){
    data1 = 20 // 오류
}

Kotlin 안드로이드 앱 프로그래밍 (4)

변수 선언
코틀린 내에서 변수 선언은 val , var 키워드로 선언합니다. val은 value의 줄임말로 초깃값이 할당되면 바꿀 수 없는 변수를 선언할 때 사용하고, var은 variable의 줄임말로 초깃값이 할당된 후에도 값을 바꿀 수 있는 변수를 선언할 때 사용합니다.
val(value) = 값 할당 시 바꿀 수 없는 변수 선언할 때 사용
var(variable) = 값 할당된 후에도 바꿀 수 있는 변수 선언할 때 사용

val(혹은 var) 변수명 : 타입 = 값 
val과 var 변수의 차이

val data1 = 10
var data2 = 10

fun main(){
    data1 = 20 //오류
    data2 = 20 //성공
}

data1과 data2에 변수를 선언하였고, data1은 val 키워드로 선언했으며 초깃값을 할당한 후 main() 함수에서 값을 변경하려고 시도합니다. 여기서 컴파일 오류가 발생합니다. val 키워드로 선언한 변수는 처음 할당한 값으로만 이용할 수 있습니다. 그런데 data2 변수는 var 키워드로 선언했으므로 값을 변경할 수 있습니다.
val = 초깃값 할당 후 변경불가
var = 초깃값 할당 후 변경가능 [위 내용 참고]

타입 지정과 타입 추론
변수명 뒤에는 콜론을 추가하여 타입을 명시할 수 있으며, 대입하는 값에 따라서 타입을 유추 할 수 있을 때는 생략 가능합니다.

val data1: Int = 10
val data2 = 10     -- (val data2: Int = 10) 유추 가능하면 생략가능

data1은 명시적으로 Int 타입을 선언한 것이며 data2는 대입한 값이 10 이므로 타입을 명시하지 않아도 자동으로 Int 타입이 됩니다. 즉 val data2: Int = 10이라고 선언한 것과 똑같습니다.

초깃값 할당
최상위에 선언한 변수나 클래스의 멤버 변수는 선언과 동시에 초깃값을 할당해야 하며, 함수 내부에 선언한 변수는 선언과 동시에 초깃값을 할당하지 않아도 됩니다. 물론 변수를 이용하려면 값을 할당하고 이용해야 합니다.

변수-클래스 멤버변수 선언과 동시에 초깃값을 할당
함수 내부에 선언한 변수 선언과 동시에 초깃값 할당하지 않아도 됨

val data1: Int //오류
val data2 = 10 //성공

fun someFun(){
    val data3: Int
    println("data3 : $data3")  //오류
    data3 = 10
    println("Data3 : $data3") //성공
}

class User{
    val data4: Int //오류
    val data5: Int = 10 //성공
}

**초깃값 할당하기**

초기화 미루기
변수를 선언할 시 초깃값을 할당할 수 없는 경우가 있습니다. 이때는 값을 이후에 할당할 것이라고 컴파일러에게 알려 주어야 합니다. lateinit나 lazy 키워드를 이용하여야 합니다.
lateinit 키워드는 이후에 초깃값을 할당할 것임을 명시적으로 선언합니다.

lateinit var data1: Int    //오류
lateinit val data2: String //오류
lateinit var data3: String //성공

lateinit 2가지 규칙
1. lateinit은 var 키워드로만 변수 선언 사용 가능합니다.
2. Int,Long,Short,Double,Float,Boolean,Byte 타입에는 사용할 수 없습니다.

lazy 키워드는 변수 선언문 뒤에 by lazy {} 형식으로 선언하며, 소스에서 변수가 최초로 이용되는 순간 중괄호로 묶은 부분이 자동으로 실행되어 그 결괏값이 변수의 초깃값으로 할당됩니다. lazy 문의 중괄호 부분을 여러 줄로 작성한다면 마지막 줄의 실행 결과가 변수의 초깃값이 됩니다.

val data4: Int by lazy{
    println("in lazy.....")
    10
}

fun main(){
    println("in main.....")
    println(data4 + 10)
    println(data4 + 10)
}

결과값
in main.....
in lazy.....
20
20

데이터 타입
코틀린이 제공하는 데이터 타입
코틀린의 모든 변수는 객체, 따라서 코틀린의 모든 타입은 객체 타입입니다. 정수를 다루는 타입이 Int인데 Int는 기초 데이터 타입이 아니라 클래스 입니다.

fun someFun(){
    var data1: Int = 10    
    var data2: Int? = null  // null 대입 가능

    data1 = data1 + 10
    data1 = data1.plus(10)  // 객체의 메서드 이용 가능
}

data1과 data2 변수를 Int 타입으로 선언했습니다. 만약 Int 타입이 기초 데이터 타입이라면 변수에 null을 대입할 수 없으며 메서드를 호출할 수도 없습니다. 하지만 코틀린의 모든 타입은 객체이므로 Int 타입의 변수에 10이라는 정수 뿐만 아니라 null을 대입할 수도 있습니다. 또 한 객체의 메서드도 호출할 수 있습니다.
** null은 객체가 선언만 되고 메모리 할당이 되지 않았음을 의미합니다. **

기초 타입 객체
(Int Short Long Double Float Byte Boolean)
기초 데이터를 객체로 표현하는 타입입니다. 정수를 표현하는 Int, Short, Long과 실수를 표현하는 Double, Float, 2진수를 표현하는 Byte, 그리고 true나 false를 표현하는 Boolean 타입이 있습니다.

val a1: Byte = 0b00001011

val a2: Int = 123
val a3: Short = 123
val a4: Long = 10L
val a5: Double = 10.0
val a6: Float = 10.0f

val a7: Boolean = true

Kotlin 안드로이드 앱 프로그래밍 (3)

코틀린의 등장 배경
코틀린은 젯브레인스(JetBrains)에서 오픈소스 그룹을 만들어 개발한 프로그래밍 언어입니다. 코틀린은 2011년에 처음 공개되었으며 2017년 구글에서 안드로이드 공식 언어로 채택함에 따라 알려지게 되었습니다. 코틀린은 러시아의 섬에서 이름을 따와 명명되었습니다.
코틀린으로 안드로이드 앱개발을 할수있는 이유는 자바의 JVM에 기반을 둔 언어이기 때문입니다.

코틀린은 자바와 유사하지만 다른 언어입니다. 확장자도 (.java / .kt) 로 다릅니다. 하지만 코틀린 컴파일러가 .kt 파일을 컴파일하면 자바 바이트 코드가 만들어집니다. 즉, 개발자는 자바와 다른 코틀린으로 코드를 작성하지만 컴파일하면 자바 클래스가 만들어지고 이를 JVM이 실행합니다. 이 이유로 코틀린이 자바를 대체할 목적으로 만든 언어라고 소개되고 있다.

코틀린으로 안드로이드 앱 개발을 하게 된다면 장점은?
1) 표현력과 간결함
2) 안전한 코드
3) 상호 운용성
4) 구조화 동시성

코틀린의 파일 구성
코틀린의 파일 확장자는 .kt 입니다. 다음 아래의 예시는 코틀린의 대체적인 파일 구성입니다.
(User.kt)

package com.example.test3 (패키지)

import java.text.SimpleDateFormat (임포트)
import java.util.*

var data = 10 (변수)

fun formatData(date: Date): String{ (함수)
    val sdformat = SimpleDateFormat("yyyy-mm-dd")
    return sdformat.format(date)
}

class User{ (클래스)
      var name = "hello"
      
      fun sayHello(){
          println("name : $name")
      }
}

package 구문은 이 파일을 처음 컴파일했을 때 만들어지는 클래스 파일의 위치를 나타냅니다. 소스파일에서는 맨 처음 한 줄로 선언합니다. 이 파일은 com/example/test3 폴더에 생성됩니다. 그런데 package 이름은 kt 파일의 위치와 상관없는 별도의 이름으로도 선언할 수 있습니다. 예를 들어 위의 User.kt 파일이 com/example/test3에 있더라도 package ch3 처럼 선언할 수 있습니다. 물론 이렇게 선언했을 때 컴파일된 클래스 파일은 ch3 폴더에 생성됩니다.

import 구문은 package 구문 아래에 여러 줄 작성할 수 있습니다. 그리고 import 문 아래에 변수, 함수, 클래스를 선언할 수 있습니다. 변수와 함수는 클래스 안 뿐만 아니라 클래스 밖에도 선언할 수 있습니다. 그리고 어떤 파일에 선언한 변수, 함수, 클래스를 다른 코틀린 파일에서 참조할 때 두 파일을 같은 package로 선언했다면 import 없이 사용할 수 있습니다.

[코틀린 파일명을 클래스명과 다르게 선언해도 되는 이유?]
코틀린 파일명과 파일 내에 선언한 클래스명과는 상관이 없습니다. 코틀린은 객체지향만을 목적으로 한 언어가 아니라서 그렇습니다. 객체지향만 지원하는 언어라면 모든 것이 클래스로 묶여야 하지만 코틀린은 변수, 함수 등을 클래스로 묶지 않고 최상위에 선언할 수 있으므로 파일명과 클래스명은 아무런 상관이 없습니다.

[코틀린의 변수, 함수를 클래스로 묶지 않고 선언한다면 자바에서 이용할 때 문제는?]
아무런 문제 없습니다. 코틀린에서는 변수, 함수를 최상위에 선언할 수 있으므로 하나의 코틀린 파일에 선언한 멤버를 다른 코틀린 파일에서 이용할 때는 변수, 함수, 클래스에 각각 접근하면 됩니다. 그런데 자바에서는 모든 것을 클래스로 묶어야 합니다. 즉, 자바에서는 최상위에 클래스만 선언할 수 있으며 변수나 함수는 최상위에 선언할 수 없습니다. 따라서 코틀린에서 최상위로 선언한 변수나 함수를 자바에서 이용할 수 없을 것 처럼 보입니다. 하지만 코틀린 컴파일러가 가능하게 해줍니다.

코틀린 소스를 테스트하는 방법
코틀린 소스 파일의 main()함수를 실행하는 코드입니다. 안드로이드 스튜디오에서 실행하는 방법입니다.
프로젝트 실행 -> 탐색 창에서 java 디렉터리 아래 패키지명으로 된 경로를 마우스 오른쪽으로 클릭하고 [New -> Kotiln Class/File -> File]을 선택한 후 적당한 이름으로 코틀린 파일을 만듭니다. 테스트할 코틀린 소스 파일에는 다음처럼 main() 함수가 있어야 하며, 이 소스 파일을 실행하면 main() 함수가 자동으로 실행됐다가 끝나면 프로그램이 종료 됩니다.

fun main(){                                                       [Test.Kt]
    println("hello world")
}

안드로이드 스튜디오에서 탐색 창에서 main() 함수가 있는 코틀린 파일을 마우스 오른쪽으로 선택한 후 [Run ‘TestKt’] 메뉴를 누릅니다. 메뉴 이름에서 ‘TestKt’는 파일명에 ‘Kt’자가 붙은 것 입니다.

실행 메뉴를 선택하면 안드로이드 스튜디오 아래쪽 [Run] 창에 실행 결과가 출력됩니다.

Kotlin 안드로이드 앱 프로그래밍 (2)

안드로이드 소개
안드로이드(Android)는 리눅스를 기반으로 구글에서 제작된 모바일 운영체제입니다. 흔히
우리나라 휴대폰 시장에서는 삼성 – 애플 – 화웨이 등 독자적인 os인 ios를 탑재중인 애플
외에는 안드로이드를 채택하여 제작 출시후 판매중입니다. 2008년 처음 출시 후 계속 새 버전
을 내놓아 전 세계 모바일 플랫폼 시장에서 많은 비율로 차지하고 있습니다.

안드로이드의 특징
1. 안드로이드는 공개 운영체제인 리눅스를 기반으로 합니다.
2. 안드로이드 앱은 Kotlin or Java를 이용해 개발합니다.
3. 안드로이드 운영체제의 주요 부분과 라이브러리, 구글에서 만든 앱 등의 코드는 대부분
공개 되어 있습니다.
4. 안드로이드 스마트폰은 구글 뿐만 아니라 여러 제조업체에서 만들수 있습니다.
(위에 안드로이드 소개 문단 참조)
5. 안드로이드 앱은 다양한 방법으로 사용자에게 배포가 가능합니다.

안드로이드 운영체제의 구조

[리눅스 커널] 안드로이드는 리눅스에 기반을 둔 오픈소스 소프트웨어 스택입니다.
[하드웨어 추상화 레이어] 하드웨어의 추상화 계층으로, 상위의 자바 API 프레임워크에서
                       하드웨어 기능을 이용할 수 있게 표준 인터페이스를 제공합니다.
[안드로이드 런타임] 흔히 ART라고 하며 앱을 실행하는 역할을 합니다.
[네이티브 C/C++ 라이브러리] 안드로이드 앱은 대부분 자바 프레임워크로 개발하지만 네이
                           티브 C/C++ 라이브러리를 이용할수 있는데 이를 안드로이드
                           NDK라고 합니다.
[자바 API 프레임워크] 앱을 개발할때 사용하는 자바 API 입니다.

앱 개발 언어
원래 안드로이드가 초기에 나왔을때 부터는 Java로 앱 개발을 해왔었는데 2017년 구글 IO 행사에서 Kotlin을 안드로이드 공식 언어로 지정하면서 이제 안드로이드 개발 언어로는 Java와 Kotlin을 사용합니다.

다양한 디바이스
ios os를 탑재한 아이폰은 애플에서만 제작할수 있지만 안드로이드폰은 다양한 제조업체에서 만들 수 있습니다. (삼성전자, 화웨이 등) 사용자는 다양한 휴대폰을 접할수 있어서 장점이지만 개발자는 주의해야할점이 있습니다.
다양한 제조업체에서 출시된 휴대폰에서 앱이 똑같이 보이도록 호환성을 고려해야 합니다. 또한 제조업체에서 휴대폰을 생산할 때는 구글에서 만든 운영체제 API와 주소록 같은 기본앱 등을 조금씩 바꾸므로 자신이 만든 앱이 여러 장치에서 제대로 동작하는지를 주의 및 점검해야 합니다.

안드로이드 버전
구글은 2008년 안드로이드 1.0 버전을 출시한 이후 새로운 버전을 출시하고 있습니다. 새 버전을 출시하면서 새로운 기능이 추가되거나, 기존 API가 변경 또는 제거되는 등 앱 개발에 영향을 미치는 변화가 뒤따랐습니다. 따라서 새로운 안드로이드 버전이 나오면 개발자는 변경사항을 파악해서 앱에 적용해야 합니다.
또, 안드로이드 버전은 11.0 12.0처럼 운영체제 버전을 가리키지만 앱을 개발할 때 사용하는 버전은 API 레벨(SDK 버전)입니다. 운영체제 버전별로 API 레벨이 지정돼 있어서 소스 코드에서는 대부분 API 레벨을 이용합니다. 따라서 개발자는 운영체제 버전과 API 레벨을 함께 알고 있어야 합니다.
코드명은 안드로이드 버전의 별칭으로 사용하는데, 2012′ Android Jelly Bean , 2014′ Android Lollipop 등 코드명으로 부릅니다. 그런데 구글에서는 안드로이드 10.0 버전부터 코드명을 사용하지 않겠다고 선언했습니다.

-> Q) 새로운 버전이 나오더라도 이전 버전과 호환이 될텐데 새로운 내용을 앱에 적용할 필요
가 있는가?
-> A) 이전 버전에서 사용한 API가 변경될수 있기때문에 새로 추가된 API를 적용하지 않으면
새버전을 탑재한 휴대폰에서 앱이 동작하지 않을수도 있기 때문입니다.

안드로이드 앱 개발의 특징
컴포넌트가 제일 중요합니다. 안드로이드 앱 개발의 구조를 이해하려면 컴포넌트가 무엇이고 어떻게 동작하는지 반드시 알아야 합니다.

컴포넌트는 애플리케이션의 구성요소
컴포넌트를 쉽게 정의하자면 애플리케이션의 구성요소라고 할수 있습니다.
컴포넌트는 애플리케이션을 구성하는 단위입니다. 즉, 하나의 애플리케이션은 여러 컴포넌트로 구성됩니다. 컴포넌트가 어떤 형태인지는 상황에 따라 달라지는데 JAR 파일이나 DLL로도 개발합니다.
안드로이드 앱의 기본구조도 컴포넌트에 기반을 두므로 하나의 앱은 여러 컴포넌트로 구성됩니다. 그리고 안드로이드에서는 클래스로 컴포넌트를 개발합니다. 즉, 하나의 클래스가 하나의 컴포넌트가 되는것입니다.

안드로이드 컴포넌트 종류

[액티비티] 화면을 구성하는 컴포넌트
[서비스] 백그라운드 작업을 하는 컴포넌트
[콘텐츠 프로바이더] 앱의 데이터를 공유하는 컴포넌트
[브로드캐스트 리시버] 시스템 이벤트가 발생할 때 실행되게 하는 컴포넌트

구분방법
컴포넌트는 앱이 실행될 때 안드로이드 시스템에서 생명주기를 관리하는 클래스지만 개발자가 만들어야하는 클래스입니다. 개발자가 컴포넌트 클래스를 만들 때는 지정된 클래스를 상속받아야 하는데 이 상위 클래스를 보고 구분할 수 있습니다.
액티비티는 Activity
서비스는 Service
콘텐츠 프로바이더는 ContentProvider
브로드캐스트 리시버는 BroadcastReceiver 클래스를 상속받아서 만듭니다.

앱 개발할 때 컴포넌트 구성
컴포넌트는 개발자가 만들고자 하는 앱의 기능과 화면 등을 고려해 필요한 만큼 구성하면 된다. 앱을 개발할 때 어떤 컴포넌트를 어떻게 구성하는지는 설계에 따라 달라지며 정해진 규칙은 없습니다. 심지어 액티비티가 없는, 그래서 사용자에게 화면을 제공하지 않는 앱도 개발할 수 있습니다.

Kotlin 안드로이드 앱 프로그래밍 (1)

학기중 수강한 프로그래밍 언어(python, C, Java) 외에 새로운 프로그래밍 언어에 대해 학습하고 싶었습니다. 이미 수강했던 Java 와의 언어적 상호관계가 있는 Kotlin 언어를 새롭게 학습해 보는것으로 하며 그리고 이번 학기에 수강하는 앱프로그래밍 과목도 배우니 혼자서 공부하면 꿩먹고 알먹고 좋을꺼같아서 Kotlin 안드로이드 앱 프로그래밍을 한번 공부해보는것으로 결정하였습니다.

1. 개발 환경 준비

구글은 13′ 에 안드로이드 전용 앱을 개발하는 도구로서 ‘안드로이드 스튜디오’ 를 발표하였습니다. 안드로이드 앱을 개발하기 위해서는 거의 필수적입니다. 안드로이드 개발자 사이트에서 (Android 스튜디오)를 손쉽게 내려받아 설치할수 있습니다. 흔히 각 프로그래밍 언어의 개발환경 파이참, vscode, eclipse 등 설치 과정과 유사합니다. 손쉽게 따라할수 있습니다.

2. 첫번째 앱 만들기

New project 선택 -> 새로운 프로젝트 생성
앱이 실행될 플랫폼과 템플릿을 선택해야합니다. 안드로이드는 다양한 실생활 방면에서 구동됩니다. 휴대폰, TV, 자동차 등 다양합니다.
Phone and Tablet을 선택하고 템플릿은 빈 화면이 기본인 Empty Activity를 선택합니다.

그다음 프로젝트 정보를 입력합니다.
Name / Packge name / Save location / Language / Minimum SDK 가 있습니다.

[Name] 프로젝트나 패키지 이름은 개발자가 직접 정할수 있습니다.

[Packge name] 앱의 식별값입니다. 안드로이드 앱은 개발자가 작성한 패키지명으로 식별됩니다. 동일한 패키지
              명으로는 스토어에 등록될수도 없고 기기에 설치도 불가능 합니다. 패키지명은 대체로 도메인을
              역순으로 입력하고 끝에 프로젝트명을 붙이는 형태로 작성합니다.

[Save location] 프로젝트의 파일들이 저장되는 루트 디렉터리입니다. 앱 개발을 진행하면서 소스나 이미지
                파일들이 이곳에 저장됩니다.

[Language] 기본값은 Kotlin 물론 Java로도 가능합니다.

[Minimum SDK] 앱이 설치되는 최소 SDK 버전입니다. 설정해서 개발된 앱은 설정한 버전 이상의 휴대폰에서만
              설치됩니다.

SDK 매니저
안드로이드 스튜디오 화면의 오른쪽 위를 보면 툴바에 아이콘이 있습니다. 안드로이드 SDK를 관리할수 있는 SDK 매니저가 열립니다.
SDK Platforms에서는 안드로이드 SDK 목록을 보여줍니다. 안드로이드 스튜디오를 설치할때 최신버전의 SDK가 기본으로 설치됩니다. 최신버전의 SDK를 설치해서 개발할 필요가 있으나 호환성 문제로 낮은 버전에서 테스트 하기 위해서 다른 버전의 SDK를 설치할수도 있습니다.
SDK Tools를 클릭하면 개발자 도구들이 표시됩니다. 기본적으로 필요한 도구는 안드로이드 스튜디오를 설치하면 기본적으로 포함됩니다.

SDK 도구

Android SDK Bulid-Tools 32-rc1 / 앱을 빌드하는 데 필요한 도구 (필수설치)
Android Emulator / 앱을 실행하는 데 필요한 도구 (필수설치)
Android SDK Platform-Tools / 안드로이드 플랫폼과 연동되는 adb,fastboot,systrace와 같은 도구 모음 (필수설치)
Android Emulator Hypervisor Driver for AMD Processors(Installer) / AMD용 하이퍼바이저 드라이버 (AMD CPU 설치용)
Intel x86 Emulator Accelerator(HAXM intaller) / 인텔 에뮬레이터 가속기 (인텔 CPU 설치용)

위에 필수설치 3가지는 반드시 설치해야하며 AMD-인텔 설치용은 자신의 CPU에 따라 설치하면 됩니다.

3. 앱 실행하기
앱을 테스트 실행하는 방법을 살펴보겠습니다. 2가지 방법이 있습니다.
[안드로이드 스튜디오가 제공하는 가상기기] [실제 스마트폰을 이용] 2가지 방법이 있습니다.
이중 가상기기인 에뮬레이터 방식만 설명하겠습니다.

가상 기기에서 실행
안드로이드 가상 기기는 AVD 라고 하며 에뮬레이터라고 부릅니다. AVD 매니저에서 Create Virtual Device를 클릭하면 에뮬레이터 만들기를 시작합니다.
하드웨어 크기인 스마트폰 선택을 할수 있으며 여러 종류가 있습니다. 그 다음으로 시스템 이미지를 선택하는 창이 나옵니다. AVD 설정에서 이 부분이 가장 중요한데 앞에서 선택한 하드웨어에 설치할 시스템 이미지, 즉 안드로이드 운영체제 버전을 선택해야합니다. 모든 설정을 확인한 다음에 AVD 만들기를 완료하면 됩니다.

에뮬레이터 실행
이렇게 AVD를 만들면 매니저 창에 추가됩니다. 여기서 Actions 항목에 있는 버튼을 누르면 실행, 수정, 기타 메뉴를 실행할 수 있습니다.

애뮬레이터에서 앱 실행
AVD까지 준비했으므로 앱을 실행해 보겠습니다. 툴바에서 실행할 앱을 선택하고 오른쪽에서 앱을 실행할 기기를 선택하고 재생버튼을 클릭하면 앱을 빌드한 후 실행합니다.