Kotlin ) intelliJ IDE 설치, Kotlin 기초
Programming/기초 지식

Kotlin ) intelliJ IDE 설치, Kotlin 기초

728x90

목차

     


     

    1. IntelliJ다운로드 및 설치

    모두 체크하면 된다

     


     

    2. 프로젝트 생성

    다운로드가 완료되면

    days01 패키지 생성
    위 경로로 클릭하고
    위와같이 파일을 생성한다.

     


     

    3. 코틀린 기초

    3.1. 출력문

     

     


     

    3.2. 리터럴

    3.2.1. 리터럴의 종류

    리터럴 : 코드 작성시에 값을 작성하는 문법

    • 정수(Int, Long) : 100, 100L, 1_000_000
      • 정수 자료에 L이 붙으면 Long 형 자료를 의미한다.
      • 큰 값의 정수를 구분하기 편하게 하기위해 천 단위 구분기호(,)와 같은 기능으로 '_'를 중간에 넣을 수 있다.
    • 실수(Double, Float) : 11.11, 22.22F
      • 'F'가 사용되면 Float형 자료이다.
    • 문자(Char) : 'A', 'B'
    • 문자열(String) : "문자열"
    • 진위 논리 리터럴 : println(false)
    //정수 리터럴
    println(100)                // Int
    println(1000000000000000)   // Long
    println(1000000000000000L)
    println(938_545_245_245);   
    //실수 리터럴
    println(11.11)
    println(22.22F)
    // 문자 리터럴
    println('A')
    println('가')
    // 문자열 리터럴
    println("문자열")      // String
    //진위 논리 리터럴
    println(true)
    println(false)
    // 참고로 '자료형은 모두 첫글자 대문자로 시작'한다.
    
    // 복수행 String
    println("동해물과 백두산이\n마르고 닳도록\n하느님이 보우하사\n우리나라 만세\n")
    println("""동해물과 백두산이
        |마르고 닳도록
        |하느님이 보우하사
        |우리나라 만세""".trimMargin())
    // .trimMargin() : 호출객체가 되는 문자열에 각행의 앞뒤공백 및 행의 시작문자 ('|')를 제거해주는 함수.
    // 큰 따옴표 세개를 이용하여 복수행의 문자열을 하나의 데이터로 다룰 수 있다.

     

    3.2.2. 변수와 자료형

    자료형의 이름 첫 글자는 대문자로 쓰여진다.

    • 정수 : Long(8), Int(4), Short(2), Byte(1)
    • 부호가 없는 정수형 : ULong(8), UInt(4), UShort(2), UByte(1)
      • U가 붙은 자료형은 'Unsigned를 의미'하며, '음수가 없는 자료형'을 칭한다.
      • Int가 -21억 ~ 21억의 표현범위라면, UInt는 0~42억까지의 표현범위를 갖는다
    • 실수 : Double(8), Float(4)
    • 논리 : Boolean(1)
    • 문자 : Char(2)
    • 문자열 : String

     

    3.2.3. 변수의 선언

    • var : 변수 선언 후 다시 값을 담을 수 있다.
    • val : 변수 선언 후 다시 값을 담을 수 없다.

    변수 선언 방식 -> var/val 변수명:타입 = 값
    타입을 생략 시 저장하는 값에 따라 자료형이 자동으로 결정된다.

    // Int a = 100;     // - 자바의 선언 방식
    val a:Int = 100; // - 코틀린의 선언 방식
    val a1:Int = 100        // 변수명 뒤에 ':'를 쓰고 자료형을 이어서 씀으로써 변수를 선언한다
    println("a1 = ${a1}")   // ${}안에 변수명을 넣어 변수의 값을 출력문구와 함께 출력한다
    println("a1 = $a1")     // 리터럴 출력이 아닌 '변수 출력'인 경우 ${}에서 {}를 생략할 수 있다.
    val a2:Int = 100        // 변수 선언시 자바스크립트처럼 자료형을 생략하면 저장하는 값에 따라 자동으로 자료형이 결정된다.
    println("a2 = $a2")

     

    var a3:Int = 100
    val a4:Int = 100
    
    // var변수는 값을 바꿀 수 있고, val은 초기화된 값을 변경할 수 없다.
    a3 = 200
    println("var a3 : $a3")
    
    // val변수는 값을 다시 저장하지 못한다.
    // a4 = 200 //에러발생
    println("val a4 : $a4")

     

    3.2.4. 변수의 null check

    • 코틀린은 변수 선언 시 '그 변수가 null값을 가질 수 있는 변수인지, 아닌지 결정이 가능'하다.
    • 일반적인 변수 선언은 null값을 허용하지 않는다.
    • 이것은 프로그래밍시 null값이 저장되어 발생하는 오류를 방지하기 위함.
    • 변수 선언 시, '자료형 뒤에 '?' 를 붙여 null을 허용'하게 할 수 있다.
    var/val 변수명:자료형 = 값  // -> null 허용하지 않는 변수
    var/val 변수명:자료형? = 값 // -> null 허용 변수

     

    // var a5:Int=null  //에러발생
    var a5:Int=100
    println("a5 : $a5")
    
    var a6:Int? = null
    println("a6 : $a6")
    
    var a7:Int? = a5
    println("a7 : $a7")
    
    // var a8:Int =a6  //에러
    // a8은 null을 허용하는 변수가 아니므로 아래 명령은 에러
    
    // * 코틀린은 모든 기본 타임 변수 및 값을 객체로 변환하여 사용합니다. *

     


     

    3.3. Function

    fun 함수이름(매개변수) : 리턴타입 {
    	코드들..
    }

     

    3.3.1. 기본 함수(전달인수x, 반환x)

    fun main(){
        test1();
    }
    
    // 기본함수 (전달인수 없고 반환값이 없는 함수)
    fun test1(){
        println("test1함수 호출")
        println("-------------")
    }

     

    3.3.2. 전달인수가 있는 함수

    fun main(){
         val a3:Int=200
         test2(50, 12.23);   // test2()함수 호출
         // 매개변수가 있는 함수를 호출할 때, 전달인수를 넣는 동작이 하나하나 끝날 때 마다 '매개변수명 : '을 인텔리제이가 전달인수 앞에 붙여준다.
         // 자동으로 입력되는 사항이니, 수동으로 입력ㄴㄴ
    
         test2(a3, 12.23);   // test2()함수 호출
         // 변수가 전달인수로 전달될 때는 앞에 매개변수명이 붙지 않는다.
    
         test2(a2=123.12, a1=300);
         // 다른 프로그래밍 언어와 마찬가지로, 전달인수의 순서는 매개변수의 기술된 순서에 맞춰 작성해야한다.
         // 다만 위와 같이 매개변수의 순서를 바꾸고자 한다면, '매개변수명='을 작성하여, 전달인수와 매개변수의 짝을 맞춰주면 가능하다.
    }
    
    fun test2(a1:Int, a2:Double ){
        println("test2함수 호출")
        println("a1:$a1")
        println("a2:$a2")
        println("-------------")
    }

     

    3.3.3. 매개변수에 기본값(defaultr값)이 지정된 함수

    fun main(){
         test3(100, 11.11);
         test3(200);
         // a2에 전달할 값이 생략되어 기본값 0.0이 a2변수에 대입된다.
         test3(a2 = 30.123)
         // a1에 전달할 값을 생략하려면 매개변수 이름을 써서 전달값만 전달한다.
         test3()
         // 모두 생략도 가능하다(default값이 있기 때문!)
    }
    
    // 매개변수에 기본값(디폴트)가 지정된 함수 : 해당 매개변수에 전달된 값이 없으면, 기본값이 변수에 대입된다.
    fun test3(a1:Int = 0, a2:Double=0.0 ){
        println("test3함수 호출")
        println("a1:$a1")
        println("a2:$a2")
        println("-------------")
    }

     

    3.3.4. return(반환값)이 있는 함수

    toInt() : 변수 또는 리터럴의 형변환(toInt -> Int로 변환)

    fun main(){
         val r1:Int = test4(100,200.12)
         println("r1 : $r1")
         val r2:Int = test4(1000, 2000.123)
         println("r2 : $r2")
    }
    
    // 리턴(반환값)이 있는 함수
    // fun 함수 이름(매개변수):리턴값의 자료형 { }
    fun test4(a1:Int, a2:Double ) : Int {
        println("test4 호출")
        val result:Int = a1 + a2.toInt()
        return result
    }   // 변수 또는 리터럴 .toInt() -> 변수 또는 자료형의 형변환(정수로 변환)

     

    3.3.5. 반환값이 없는 함수 (Unit)

    fun main(){
         test5()
         test6()
    }
    
    // Unit : void의 의미로 사용되며, 대부분은 생략된 채로 사용된다.
    fun test5():Unit{
        println("test5 호출")
        println("--------------------")
    }
    
    fun test6(){
        println("test6 호출")
        println("----------------------------")
    }

     

    3.3.6. 오버로딩(Overloading)

    fun main(){
         test7()
         test7(100)
    }
    
    // 함수의 오버로딩 - 매개변수의 형태와 갯수를 달리한 같은 이름의 함수들을 정의하여 사용한다.
    fun test7(){
        println("test7 호출 - 매개변수 x")
        println("------------------------------")
    }
    fun test7(a1:Int=200){
        println("test7 호출 - 매개변수 한 개(Int:$a1)")
        println("----------------------------")
    }

     

    3.3.7. 함수 내부의 함수

    fun main(){
         fun test10(){
             println("test10 호출")
         }
         test8()
         // test9()     // 에러 (불가능)
         test10()
    }
    
    // 함수 내부의 함수
    fun test8(){
        println("test8 호출")
        fun test9(){
            println("test9 호출")
        }
        test9()
    }

     


     

    4. 예제

    국어, 영어, 수학 점수를 입력 받아서 총점 평균 성적표 양식에 맞춰 출력하세요

    package days01
    
    import java.text.DecimalFormat
    
    fun main(){
        // 코틀린의 화면 입력
        // 국어, 영어, 수학 점수를 입력 받아서 총점 평균 성적표 양식에 맞춰 출력하세요
        print("국어점수 입력 : ")
        val kor:Int = readLine()!!.toInt()
        print("영어점수 입력 : ")
        val eng:Int = readLine()!!.toInt()
        print("수학점수 입력 : ")
        val mat:Int = readLine()!!.toInt()
    
        val tot:Int = sum(kor,eng,mat)
        val avg:Double = average(tot)
        prn(kor, eng, mat, tot, avg)
    
    }
    
    fun sum(kor:Int, eng:Int, mat:Int) : Int{
        return kor + eng + mat
    }
    
    fun average(tot:Int) : Double {
        return tot/3.0
    }
    
    fun prn(kor:Int, eng:Int, mat:Int, tot:Int, avg:Double):Unit{
        val df1 = DecimalFormat("#,##0.0")
        println("\t\t===성적표===")
        println("-----------------------------------")
        println("국어\t영어\t수학\t총점\t평균")
        println("-----------------------------------")
        println("$kor\t\t$eng\t\t$mat\t\t$tot\t\t${df1.format(avg)}\t")
        println("-----------------------------------")
    
    }

     


     

    5. 연산자

    5.1. 단항 연산자

    • 항이 하나인 연산자

    5.1.1. + : +양수 -> 양수 / +음수 -> 음수

    val a1 : Int = 10;  val a2:Int = -10;
    
    // + : +양수 -> 양수, +음수 -> 음수
    val r1 : Int = +a1; val r2:Int = +a2;
    println("a1:$a1, r1:$r1");
    println("a2:$a2, r2:$r2");

     

    5.1.2. - : -양수 -> 음수 / -음수 -> 양수

    val a1 : Int = 10;  val a2:Int = -10;
    
    // - : -양수 -> 음수, -음수 -> 양수
    val r3:Int = -a1;   val r4:Int = -a2;
    println("a1:$a1, r3:$r3");
    println("a2:$a2, r4:$r4");

     

    5.1.3. ! : !true -> false / !false -> true

    val a3:Boolean = true;  val a4:Boolean = false;
    
    // ! : !true -> false , !false ->true
    val r5:Boolean = !a3;   val r6:Boolean = !a4;
    println("a3:$a3, r5:$r5");  println("a4:$a4, r6:$r6");

     


     

    5.2. 이항 연산자

    val r11:Int = 10 + 3
    val r12:Int = 10 - 3
    val r13:Int = 10 * 3
    val r14:Int = 10 / 3
    val r15:Int = 10 % 3
    
    println("$r11, $r12, $r13, $r14, $r15")

     


     

    5.3. 비교 연산자

        val a14:Int = 10
        val r17:Boolean = a14 == 10;    val r18:Boolean = a14 != 10;
        println("$r17, $r18")
        val r19:Boolean = a14 == 20;    val r20:Boolean = a14 != 20;
        println("$r19, $r20")
    
        println("-------------------------------------")
    
        val a15:Int = 10
        val r21:Boolean = a15 < 20;     val r22:Boolean = a15>20
        val r23:Boolean = a15 <= 10;     val r24:Boolean = a15>=10
        println("$r21, $r22, $r23, $r24")

     


     

    5.4. IntRange

    데이터의 범위를 나타내는 관리데이터

    val r16:IntRange = 10..20 // 10부터 20까지의 영역데이터(관리데이터)
    println("r16 : $r16")   // 출력 : 10..20 내부숫자 11개를 모두 출력하려면 반복실행이 필요
    
    val r25:IntRange = 20..30
    var r26:IntRange = 50..60
    println("r25 : $r25, r26 : $r26")
    
    println("-------------------------------------")
    
    print("r16 : ")
    for(item in r16){
        print("$item ")
    }
    
    println()
    
    print("r25 : ")
    for(item in r25){
        print("$item ")
    }
    
    println()
    
    print("r26 : ")
    for(item in r26){
        print("$item ")
    }

     


     

    6. if문

    6.1. 기본 if문

    val a1:Int = 10
    
    // 기본 if문
    if(a1==10) println("a1은 10이다")
    
    if(a1!=10){
        println("a1은 10이 아니다")
    }

     


     

    6.2. else문

    val a1:Int = 10
    
    // else문 : 조건식이 만족하지 않을 경우 수행
    if(a1==10)println("a1은 10이다")
    else{
        println("a1은 10이 아니다")
    }
    
    if(a1==20)println("a1은 20이다")
    else{
        println("a1은 20이 아니다")
    }

     


     

    6.3. else-if문

    val a1:Int = 10
    
    // else if
    if(a1==5){
        println("a1은 5이다")
    }else if(a1==10){
        println("a1은 10이다")
    }else if(a1==20){
        println("a1은 20이다")
    }else{
        println("a1은 5, 10, 20이 아니다")
    }

     


     

    6.4. 코틀린에서의 if문의 변형 사용

    // 코틀린에서만 사용할 수 있는 if문의 변형 사용
    
    // 예제1
    val a6:String = if(a5==10) "10이다" else "10이 아니다"
    println("a6:$a6")
    
    println("----------------------------------")
    
    // 예제2
    val a7:String = if(a5==10){
        println("블록1 수행")
        "10입니다"     // 변수에 입력될 값은 각 경우에 해당하는 영역 맨 마지막에 작성된다.
    }else{
        println("블록2수행")
        "10이 아니다"
    }
    println("a7:$a7")

     

     


     

    7. when문

    • 자바에는 switch문이 있다면, 코틀린에는 when문이 있다.
    • 구조적으로는 비슷하지만 몇가지 차이점이 있으니 비교하며 학습해보자.
    val a1 = 10
    
    // switch-case문법과 비슷하다. 각 case를 '->'로 표시하고, break를 작성하지 않아도 된다.
    when(a1){
        1 -> println("a1은1이다")
        2 -> {
            println("수행되는 코드")
            println("a1은 2이다")
        }
        3-> println("a1은 3이다")
        else -> println("a1은 1,2,3이 아니다. a1:$a1")
    }

     

    val a2 = 3
    
    when(a2){
        1, 2 -> println("a2는 1 또는 2")
        3, 4 -> println("a2는 3 또는 4")
        5, 6 -> println("a2는 5 또는 6")
        else -> println("a2는 1 ~ 6이 아닙니다")
    }   // (,)로 구분하여 두 가지의 경우를 하나의 case로 처리 가능

     

    val a5 = 5
    
    when(a5){
        in 1..3 -> println("a5는 1~3 사이의 숫자")
        in 4..6 -> println("a5는 4~6 사이의 숫자")
        in 1..6 -> println("a5는 1~6 사이의 숫자")
        else -> println("a5는 1~6사이의 숫자가 아닙니다")
    }   // in 키워드와 .. 연산자를 이용하여 범위를 골라내는 case를 작성할 수 있다.

     

    val a6 = 20
    
    val str:String = when(a6){
        10 -> "a6는 10이다"
        20 -> {
            println("두 번째 경우의 수")
            "a6는 20이다"
        }
        30 -> "a6는 30이다"
        else -> "a6는 10~30이 아닙니다."
    }   // 각 케이스에서 선택된 값을 결과로 반환하여 변수의 값으로 활용할 수 있다.
    println("str : $str");

     

    fun main(){
    
        // setValue()메서드 활용
        val str1 = setValue(10)
        val str2 = setValue(20)
        val str3 = setValue(30)
        val str4 = setValue(40)
        
        println("str1:$str1")
        println("str2:$str2")
        println("str3:$str3")
        println("str4:$str4")
    
    } 
    
    // when이 내장되어있는 setValue()메서드 생성
    fun setValue(a1:Int)= when(a1){
            10->"a1는 10이다"
            20->"a1는 20이다"
            30->"a1는 30이다"
            else->"a6는 10~30이 아닙니다."
    }

     


     

    8. 특별한 함수식

    8.1. 자료형의 생략

    코틀린에서 변수를 선언하면 보통 변수명 옆에 콜론( : )과 함께 자료형을 기술한다.
    하지만, 변수를 선언할 때 초기화 되는 값의 자료형이 명확하다면 변수선언의 자료형은 생략이 가능하다.

    함수의 리턴값을 담는 변수에도 마찬가지

    fun main(){
    
        // 함수의 리턴값의 자료형이 Int자료형으로 명확하기 때문에 따로 변수의 자료형을 쓰지 않는다.
    
        val r3 = testFun1(100, 200)
        println("r3(testFun1()의 결과): $r3")	// r3(testFun1()의 결과): 300
    
        val r4 = testFun2(100, 200)
        println("r4(testFun2()의 결과): $r4")	// r4(testFun2()의 결과): 300
    
        val r5 = testFun3(100, 200)
        println("r5(testFun3()의 결과): $r5")	// r5(testFun3()의 결과): 300
    }
    
    // 1. 함수의 리턴값이 호출된 곳에서 리턴값을 받는 변수의 자료형을 결정한다.
    fun testFun1(a1: Int, a2: Int): Int{
        return a1 + a2
    }
    
    // 2. 함수의 리턴값이 간단한 수식으로 해결된다면 '='와 함께 연결하여 return명령을 대신한다.
    // testFun1과 testFun2는 같은 동작을 수행.
    fun testFun2(a1: Int, a2: Int): Int = a1 + a2
    
    // 3. 리턴값의 자료형이 아래와 같이 정수와 정수의 연산이어서 그 결과도 정수라면,
    //  그리고 그 정수가 리턴될 것이라면 함수의 리턴자료형도 생략이 가능하다.
    fun testFun3(a1: Int, a2: Int) = a1 + a2
    • testFun1()
      • 함수의 리턴값이 호출된 곳에서 리턴값을 받는 변수의 자료형을 결정
    • testFun2()
      • '='로 return명령을 대신한다.
    • testFun3()
      • 함수의 리턴자료형도 생략이 가능

     

     


     

    8.2. 람다식 선언

    함수의 간략형 표현으로 함수의 몸체를 변수에 담아 사용하도록 변형한 형태.

    val lambda1 : (Int, Int) -> Int = {a1:Int, a2:Int -> a1 + a2 }  // 람다식의 풀버전
    
    val r6 = lambda1(100, 200)
        println("r6(lambda1()의 결과): $r6")	// r6(lambda1()의 결과): 300

    { a1:Int, a2:Int -> a1 + a2 } 앞에 정의된 자료형에 맞춰 사용되는 실제 매개변수와 연산식 & 리턴값

    • (Int, Int) : 매개변수의 갯수와 자료형
    • Int = {  ,  -> a1 + a2 } : 리턴값의 자료형과 리턴값
    • lambda1 : 함수명

     

    8.2.1. 함수의 원형(헤더)을 생략한 형태

    val lambda2 = {a1:Int, a2:Int -> a1 + a2 }  // 매개변수, 리턴값의 자료형이 명확하다면 ':'이후부터 '='이전 까지 생략이 가능ㅎ다.
    // (Int, Int) -> Int : '함수의 원형(헤더)을 생략'한 형태
    
    val r7 = lambda2(100, 200)
    println("r7 : $r7")	// r7 : 300

     

    8.2.2. 함수 바디의 자료형을 생략한 형태

    val lambda3 : (Int, Int) -> Int = {a1, a2 -> a1 + a2}
    // 헤더를 생략하지 않고, 함수 바디 쪽 자료형을 생략한 형태
    
    val r8 = lambda3(100, 200)
    println("r8 : $r8") // r8 : 300

     

    8.2.3. 함수 바디가 다중라인인 형태

    // 함수의 몸체에 여러라인의 명령이 필요한 경우 원래 함수 형태 호출
    val lambda4 = { a1:Int, a2:Int ->
        var r1 = a1 + a2
        var r2 = a1 + a2
        r1 * r2     // 맨 마지막에 리턴값을 쓰면, 람다함수의 리턴값으로 인식된다.
    }
    
    val r9 = lambda4(100, 200)
    println("r9 : $r9") // r9 : 90000

     


     

    9. Anonymous Function ( 익명함수 )

    두 개의 매개변수를 곱하여 리턴하는 익명함수를 생성한다.

    // 익명함수 생성
    val t1 = fun(x1:Int, x2:Int):Int{
        return x1 * x2
    }

     

    9.1. '익명함수가 전달인수'인 형태

    fun main(){
    
        // 익명함수 생성
        val t1 = fun( x1:Int, x2:Int ): Int{
            return x1 * x2
        }
    
        // 익명함수를 전달인수로 전달
        testFunc1( t1, 100, 200 ) // a3 : 20000
    
    }
    
    // 매개변수 중 하나는 함수를 저장할 수 있는 매개변수이다.
    // 함수의 '매개변수에 람다함수 헤더부분을 전달된 함수에 맞게 매개변수명 함께 작성'한다
    fun testFunc1( funcName:(Int, Int) -> Int, a1:Int, a2:Int) : Unit {
        // var a3 = funcName(100,200)
        var a3 = funcName(a1, a2)
        println("a3 : $a3")
    }

     


     

    9.2. '전달인수에 익명함수를 기술'한 형태

    fun main(){
    
        // 전달인수로 직접 익명함수를 기술
        testFunc1( fun( x1:Int, x2:Int ):Int{ return x1-x2 }, 300, 400 ) // a3 : -100
    
    }
    
    // 매개변수 중 하나는 함수를 저장할 수 있는 매개변수이다.
    // 함수의 '매개변수에 람다함수 헤더부분을 전달된 함수에 맞게 매개변수명 함께 작성'한다
    fun testFunc1( funcName:(Int, Int) -> Int, a1:Int, a2:Int) : Unit {
        // var a3 = funcName(100,200)
        var a3 = funcName(a1, a2)
        println("a3 : $a3")
    }

     


     

    9.3. '전달인수로 람다함수'를 보내는 형태

    fun main(){
    
        // 전달인수로 람다함수를 보내는 경우 : testFunc1의 매개변수가 람다함수의 헤더이므로, 전달인수는 바디만 보내면 된다.
        testFunc1( { x1:Int, x2:Int -> x1 + x2 }, 500, 600 )      // a3 : 1100
        // 전달인수와 매개변수의 조합은 'funcName:(Int, Int) -> Int = {x1:Int, x2:Int -> x1+x2}'와 같다.
        
    }
    
    // 매개변수 중 하나는 함수를 저장할 수 있는 매개변수이다.
    // 함수의 '매개변수에 람다함수 헤더부분을 전달된 함수에 맞게 매개변수명 함께 작성'한다
    fun testFunc1( funcName:(Int, Int) -> Int, a1:Int, a2:Int) : Unit {
        // var a3 = funcName(100,200)
        var a3 = funcName(a1, a2)
        println("a3 : $a3")
    }

     


     

    9.4. '리턴값이 함수인 함수'

    • 함수의 '자료형은 리턴되는 람다함수의 헤더( (Int, Int) -> Int )를 작성'한다
    • 실제 리턴되는 함수의 내용은 'return 키워드와 함께 익명함수 또는 람다함수를 작성'한다.
    fun main(){
    
        // '함수가' 리턴값으로 '리턴되어 변수에 저장'된다.
        var t2 = testFunc2()
        var r2 = t2(600, 700)   // 정수형 매개변수 두 개, 정수 리턴으로 된 함수가 리턴되어 t2에 저장되고 실행되는 형태
        println("testFunc2()가 실행되고, \n익명함수가 리턴되어 사용된 결과 : $r2")
    
    }
    
    // 리턴값이 함수인 함수의 정의
    // 함수의 리턴자료형은 리턴되는 람다함수의 헤더( (Int, Int) -> Int )를 작성한다
    // 실제 리턴되는 함수의 내용은 'return 키워드와 함께 익명함수 또는 람다함수를 작성'한다.
    fun testFunc2():(Int, Int) -> Int{
        // return fun(x1:Int, x2:Int):Int{return x1*x2}    // 익명함수가 리턴값으로 사용
        return { x1:Int, x2:Int -> x1 * x2 }   // 람다함수가 리턴값으로 사용
    }

     


    10. Loop 반복문

    10.1. for문

    코틀린의 for문은 자바의 for(시작, 끝, 증감량){ }이 아닌, 범위데이터만 사용한다.

    //  코틀린의 for문은 자바처럼 시작값 끝값 증감량 등을 사용하지 않고, 범위 데이터만 사용한다.
    val a1 = 1..10
    for(item in a1){
        print("$item\t")
    }
    
    println("\n-----------------------")
    
    //  범위 데이터로 사용되는 대상은 배열, 리스트, Map, ..을 이용한 범위데이터 등이 사용된ㄷ.
    val a2 = 1..10 step 2    // 2씩 증가하는 범위 데이터
    for(item in a2){
        print("$item\t")
    }
    
    println("\n-----------------------")
    
    //  10부터 1까지 줄어드는 범위데이터 사용
    // val a3 = 10..1   <- 잘 못된 사용 방법
    val a3 = 10 downTo 1
    for(item in a3){
        print("$item\t")
    }
    
    println("\n-----------------------")
    
    val a4 = 10 downTo 1 step 2
    for(item in a4){
        print("$item\t")
    }

     


     

    10.2. while문 ( + do-while문 )

    var a5 = 0
    while(a5<10){
        print("$a5\t")
        a5++
    }
    
    println("\n-----------------------")
    
    var a6 = 0
    do{
        print("$a6\t")
        a6++
    }while(a6<10)
    
    println("\n-----------------------")
    
    var a7 = 100
    while(a7<10){
        print("$a7\t")
        a7++
    }
    
    println("\n-----------------------")
    
    var a8 = 100
    do{
        print("$a8\t")
        a8++
    }
    while(a8<10)

     


    11. Array 배열

    11.1. arrayOf( )

    코틀린에서의 배열생성은 arrayOf( )메서드를 주로 사용한다.

    // 코틀린의 배열생성은 arrayOf를 자주 이용합니다.
    val array1 = arrayOf(10, 20, 30, 40, 50)
    println("array1:$array1")   //클래스 이름과 해시코드등이 출력된다.
    // 배열의 요소들을 한 번에 출력하려면 contentToString 함수를 사용한다.
    println("array1 : ${array1.contentToString()}")

     


    11.2. 배열의 자료형

        // 코틀린의 배열은 자료형의 제약이 없어서, 자바의 리스트 등 처럼 여러 자료형 데이터를 하나의 배열에 넣어서 사용할 수 있다.
        // 다만, 그것이 권장할 만한 동작은 아니기 때문에 자료형을 제한하는 코드가 제공되어 사용된다.
    
        //자료형 제약 없이 사용
        val array2 = arrayOf(100, 11.11, "문자열", true)
        println("array2 : ${array2.contentToString()}")
    
        // 하나의 자료로 제한하여 사용
        val array3 = intArrayOf(10, 20, 30, 40, 50) // 기본 자료형은 ArrayOf앞에 자료형을 붙여 쓰고 ( intArrayOf() )
        println("array3 : ${array3.contentToString()}")
        
        val array4 = doubleArrayOf(11.11, 22.22, 33.33, 44.44, 55.55)   // doubleArrayOf()
        println("array4 : ${array4.contentToString()}")
        
        val array5 = arrayOf<String>("문자열1", "문자열2", "문자열3")    // 객체형 자료는 제네릭을 사용한다. ( ArrayOf<> )
        println("array5 : ${array5.contentToString()}")
    
        // int array6 = new Int[5];
        // String str = new String[5];

     


     

    11.3. null 배열

    코틀린은 null을 허용하지 않는 것이 default이므로 '?'로 허용해야 한다.

    var array6 = Array(5,{0})   // 다섯개의 0이 채워진 배열 생성
    var str = Array<String?>(5,{null})  // 다섯개의 null이 채워진 배열. 코틀린의 변수는 null을 허용하지 않는 것이 기본이므로 이와같이 '?'를 넣어 null을 허용한다.
    
    println("array6 : ${array6.contentToString()}")
    println("str : ${str.contentToString()}")

     


     

    11.4. 배열 요소 꺼내기 ( for문 )

    val array1 = arrayOf(10, 20, 30, 40, 50)
    
    // 반복실행문으로 배열 요소 꺼내기
    for(item in array1){
        print("$item\t")
    }

     


     

    11.5. 배열객체의 메서드

    println("첫 번째 값 : ${array1.first()}")
    println("마지막 값 : ${array1.last()}")
    println("30의 위치 : ${array1.indexOf(30)}")
    println("평균 : ${array1.average()}")
    println("합 : ${array1.sum()}")
    println("개수 : ${array1.count()}")
    println("개수 : ${array1.size}")
    println("30을 포함하는가 : ${array1.contains(30)}")
    println("1000을 포함하는가 : ${array1.contains(1000)}")
    println("30을 포함하는가 : ${30 in array1}")
    println("1000을 포함하는가 : ${1000 in array1}")
    
    val array12 = arrayOf(5,1,3,7,10,8)
    
    val array13 = array12.sortedArray()
    println("요소를 오름차순으로 : ${array13.contentToString()}")
    
    val array14 = array12.sortedArrayDescending()
    println("요소를 내림차순으로: ${array14.contentToString()}")

     

     


    12. 달력 예제

    package days02
    
    fun main(){
        print("연도 입력 : ")
        val year:Int = readLine()!!.toInt()
        print("월 입력 : ")
        val month:Int = readLine()!!.toInt()
        print("일 입력 : ")
        val day:Int = readLine()!!.toInt()
    
        var days:Int = sumdays(year, month, day)    // 전달인수에 year, day도 추가할 것
        // sumdays 함수안에서 월에 해당하는 날짜 수 계산할 때 when을 이용하지 말고, 배열 생성 후 반복실행과 함께 계산할 것
    
        val temp:Int = days%7
        val weekday:String = selectWeekday2(temp)
        println(weekday)
    
    }
    
    fun sumdays(y:Int, m:Int, d:Int):Int{
        var days:Int = 0
        val mdays = arrayOf(0,31,28,31,30,31,30,31,31,30,31,30,31)    //배열 생성
        if(y%4==0 && y%100!=0 || y%400==0) mdays[2] = 29    // 입력한 년도가 윤년이면 2월을 29일로 변경
    
        days = 365*(y-1)
    
        val ys:IntRange = 1..(y-1)
        for(i in ys){
            if(y%4==0 && y%100!=0 || y%400==0)days++
        }   // 입력한 년도의 전년도까지 있었던 윤년만큼 +1 반복
    
        var i:Int = 0
        while(i<m){
            days+= mdays[i]
            i++
        }   // 입력한 월의 전월까지 날짜를 합산
    
        days += d   // 입력한 일만큼 날짜 합산
        return days
    }
    
    fun selectWeekday2(t:Int) = when(t){
        1->"월요일"
        2->"화요일"
        3->"수요일"
        4->"목요일"
        5->"금요일"
        6->"토요일"
        7->"일요일"
        else->"잘못된 연산"
    }

     


    13. Class ( 클래스 )

    코틀린도 자바와 마찬가지로 '객체 지향 언어'이다.

    13.1. 객체생성

    fun main(){
    
        // 생성된 클래스 형태로 객체를 생성한다.
        // 자바문법 : TestClass1 obj1 = new TestClass1();
        // 코틀린 : val 참조변수명 : 클래스명 = 클래스명()
        val obj1:TestClass1 = TestClass1()
        // 'new'를 쓰지 않는다고 새로운 공간이 만들어지지 않는 것이 아니라 '생략되어 사용될 뿐'이다.
        println("obj1:$obj1")
        
        val obj2:TestClass1 = TestClass1()  // new를 쓰지 않아도 새 공간이 할당된다.
        println("obj2:$obj2")
        
        val obj3 = obj1
        println("obj3:$obj3")
    
        val obj4 = TestClass2()
        println("obj4:$obj4")
    
    }
    
    // 클래스 선언
    class TestClass1{ }   // 비어있는 클래스
    
    class TestClass2    // 비어있는 클래스를 생성 ( {}생략 )
    // 비어있는 클래스를 생성한다면 {}를 생략할 수 있다.

     


     

    13.2. 멤버변수 / 메서드에 접근

    package days02
    
    fun main(){
    
        val obj5 = TestClass3()
        // 멤버변수에 접근하는 순간 내부적으로 getter/setter가 실행되어 값을 이용하게 된다.
        println("obj5.a1 : ${obj5.a1}") // 0
        println("obj5.a2 : ${obj5.a2}") // 100
    
        //obj.a1 = 100 // 에러 : 'setter가 없는 a1변수(val)는 값을 변경할 수 없다.'
        obj5.a2 = 200
        println("obj5.a2 : ${obj5.a2}") // 200
        obj5.testMethod1()  // testMethod1
        obj5.testMethod2()  // testMethod2
        
    }
    
    // 클래스 선언
    class TestClass3{
    
        // 멤버변수 : 멤버변수는 var 또는 val로 자유롭게 생성이 가능.
        // 자료형은 초기값에 따라 결정되도록 생략 가능하다.
        
        val a1:Int = 0      // getter만 있는 val변수는 밑줄이 없다.
        var a2:Int = 100    // getter와 setter가 모두 생성되는 var 변수는 밑줄이 표시된다.
        // 코틀린 클래스에서는 생성된 멤버변수는 기본적으로 private로 생성된다.
    
        //멤버 메서드
        fun testMethod1(){
            println("testMethod1")
        }
    
        fun testMethod2(){
            println("testMethod2")
        }
    }

     


    13. 클래스의 초기화 : init, constructor ( 생성자 )

    13.1. initializer ( init{} )

    • 코틀린은 클래스에 init블럭을 만들어주면 객체 생성시 자동으로 처리되는 코드를 만들 수 있다.
    • 멤버변수에 값을 초기화하는 기능도 가능하다.
    • init블럭은 생성자와 비슷한 역할을 할 수 있는 역영이지만, 전달인수나 기타의 함수로서의 기능은 없는 단순블럭이다.
    • init블럭은 멤버변수 초기화, 생성자는 그외 객체 생성시 해야할 일들이 실행된다.
    fun main(){
    
        val obj1 = TestClass11()
        println("obj1:$obj1")
    
    }
    
    // 코틀린은 클래스에 init블럭을 만들어주면 객체 생성시 자동으로 처리되는 코드를 만들 수 있다.
    // 멤버변수에 값을 초기화하는 기능도 가능하다.
    // init블럭은 생성자와 비슷한 역할을 할 수 있는 역영이지만, 전달인수나 기타의 함수로서의 기능은 없는 단순블럭이다.
    // init블럭은 멤버변수 초기화, 생성자는 그외 객체 생성시 해야할 일들이 실행된다.
    
    class TestClass11{
        init{
            println("init블럭 처리 : 객체가 생성되면 자동으로 동작되는 부분")
        }
    }

     


     

    13.2. constructor ( 생성자 )

    • 생성자를 만들지 않으면 내부에 존재하는 디폴트 생성자가 사용되지만, 인위적으로 꺼내어 따로 기술할 수 있다.
    • 코틀린 클래스의 생성자 이름은 클래스이름을 따르지 않고, 'constructor'로 사용된다.
    • constructor'라는 이름의 생성자는 오버로딩이 가능하다.

    13.2.1. 보조 생성자 ( 클래스 지역변수 내 선언 )

    fun main(){
    
        val obj2 = TestClass12()    // 매개변수가 없는 생성자 호출
        println("obj2:$obj2")
        println("obj2.v1:${obj2.v1}")
        println("obj2.v2:${obj2.v2}")
        val obj3 = TestClass12(100,200) // 매개변수가 있는 생성자 호출
        println("obj3:$obj3")
        println("obj3.v1:${obj3.v1}")
        println("obj3.v2:${obj3.v2}")
    
    }
    
    class TestClass12{
        var v1:Int = 0
        var v2:Int = 0
        // 생성자(constructor)를 만든다.
        // 생성자를 만들지 않으면 내부에 존재하는 디폴트 생성자가 사용되지만, 인위적으로 꺼내어 따로 기술할 수 있다.
        // 코틀린 클래스의 생성자 이름은 클래스이름을 따르지 않고, 'constructor'로 사용된다.
        constructor(){
            println("매개변수가 없는 생성자 동작")
        }
        // 'constructor'라는 이름의 생성자는 오버로딩이 가능하다.
        constructor(a1:Int, a2:Int){
            println("매개변수가 두 개인 생성자 동작")
            v1 = a1
            v2 = a2
        }
    }

     

     

    13.2.2. 주 생성자 ( 클래스 선언부에 선언 )

    • 클래스명 옆에 기술하는 생성자
    • '주(대표)생성자'라고 부른다.
    • 그 외 클래스 내부에 만들어지는 다른 오버로딩된 생성자들은 '보조생성자'라고 부른다.
    • 주생성자에서 매개변수들은 var, val 등을 붙여 선언한다.
    • 이들은 생성자의 매개변수이기도 하지만, 클래스의 멤버변수가 되기도 한다.
    • 클래스 TestClass13의 이름 옆의 생성자는 매개변수가 없는 디폴트생성자를 대체하는 생성자로서, 매개변수 없는 생성자는 현재 클래스에서 없다고 생각해야 한다.
    • 디폴트 생성자를 추가해야한다면 보조생성자로 오버로딩 되도록 생성해야 한다.
    fun main(){
    
        val obj4 = TestClass13(100,200)
        println("obj4.a1:${obj4.a1}")   // obj4.a1:100
        println("obj4.a2:${obj4.a2}")   // obj4.a2:200
        // val obj5 = TestClass13() // 에러 : 매개변수 두개 있는 생성자가 디폴트 생성자를 대체하고
        // 별도로 디폴트 생성자를 오버로딩 하지 않았으므로 이것은 에러가 발생한다.
    
    }
    
    // 클래스명 옆에 기술하는 생성자
    // 자바에는 없는 생성자이며 '주(대표)생성자'라고 부른다.
    // 그 외 클래스 내부에 만들어지는 다른 오버로딩된 생성자들은 '보조생성자'라고 부른다.
    // 주생성자에서 매개변수들은 var, val 등을 붙여 선언한다.
    // 이들은 생성자의 매개변수이기도 하지만, 클래스의 멤버변수가 되기도 한다.
    // 클래스 TestClass13의 이름 옆의 생성자는 매개변수가 없는 디폴트생성자를 대체하는 생성자로서, 매개변수 없는 생성자는
    // 현재 클래스에서 없다고 생각해야 한다. 디폴트 생성자를 추가해야한다면 보조생성자로 오버로딩 되도록 생성해야 한다.
    
    class TestClass13 constructor(var a1:Int, val a2:Int){
    }
    
    // class TestClass13 constructor(){}
    // 클래스 생성시 매개변수가 없는 생성자는 이미 존재하기 떄문에 위와같은 표현은 따로 쓰지 않는다.
    // class Test13{}
    
    // 위 생성자의 모습은 이렇게도 바뀌어 사용할 수 있다. (클래스에 더 이상 쓸 내용이 없어서 중괄호 생략)
    class TestClass14 (var a1:Int, val a2:Int)

     

    13.2.3. 주 생성자 + 보조 생성자의 오버로딩

    • ※ 클래스의 이름옆에 있는 대표생성자는 객체생성시에 반드시 한번은 호출되어야 하는 강제규칙이 있다.
    • 이는 보조생성자가 있고 보조생성자가 호출되더라도 마찬가지이다.
    • 만약 객체 생성시에 보조생성자가 호출되었다면, 해당 보조생성자에서 대표생성자를 재호출해야 에러가 없다.
    • 보조생성자에서 디폴트 생성자를 재호출하는 방법은 constructor()옆에 :this(디폴트생성자에 맞는 전달인수)형태로 호출한다.
    • 클래스 이름 옆에 대표 생성자는 "멤버변수를 포함하고 있기 때문에" 그를 호출하지 않으면 객체 생성시 멤버변수가 만들어지지 않는다는 말과 같다. 따라서 반드시 this 호출이 필요하다.
    fun main(){
    
        // val obj5:TestClass15 = TestClass15(10,20)
        val obj6:TestClass15 = TestClass15()        // 매개변수가 없는 보조생성자 호출
        val obj7:TestClass15 = TestClass15(10)  // 매개변수가 하나인 보조생성자 호출
    
    
    }
    
    // 클래스 이름 옆에 생성자를 만들고 나서 오버로딩된 다른 보조생성자를 사용하고 싶을 때
    class TestClass15(var a1:Int, val a2:Int){
        // ※ 클래스의 이름옆에 있는 대표생성자는 객체생성시에 반드시 한번은 호출되어야 하는 강제규칙이 있다.
        // 이는 보조생성자가 있고 보조생성자가 호출되더라도 마찬가지이다.
    
        // 만약 객체 생성시에 보조생성자가 호출되었다면, 해당 보조생성자에서 대표생성자를 재호출해야 에러가 없다.
        // 보조생성자에서 디폴트 생성자를 재호출하는 방법은 constructor()옆에 :this(디폴트생성자에 맞는 전달인수)형태로 호출한다.
        // 클래스 이름 옆에 대표 생성자는 "멤버변수를 포함하고 있기 때문에" 그를 호출하지 않으면 객체 생성시 멤버변수가 만들어지지 않는다는 말과 같다.
        // 따라서 반드시 this 호출이 필요하다.
    
        constructor() :this(10,20){
            println("매개변수가 없는 보조생성자 호출")
        }
        constructor(a1:Int) :this(10,a1){
            println("매개변수가 하나인 보조생성자 호출")
        }
    }

     


    14. extends ( 상속 )

    14.1. 부모 클래스를 상속하는 자식 클래스의 객체 생성

    fun main(){
        
        val s1 = SubClass1() //자식클래스 객체 생성
    
        println("s1.subMember1:${s1.subMember1}")      // s1.subMember1:200
        s1.subMethod1()                                 // SubClass1의 메서드 실행
    
        println("s1.superMember1:${s1.superMember1}")  // s1.superMember1:100
        s1.superMethod1()                               // SuperClass1의 메서드 실행
    
    }
    
    // 상속이 될 클래스들이 final로 만들어진다. 이는 상속이 불가능하므로, 'open 키워드'를 앞에 붙인다
    open class SuperClass1{
        var superMember1 = 100
        fun superMethod1(){
            println("SuperClass1의 메서드 실행")
        }
    }
    
    // 상속은 extends키워드 없이 부모클래스 이름을 ':', '()'와 함께 클래스 옆에 써준다.
    class SubClass1 : SuperClass1(){
        var subMember1 = 200
        fun subMethod1(){
            println("SubClass1의 메서드 실행")
        }
    }

     


     

    14.2. 부모 클래스에 생성자가 있는 경우 

    fun main(){
    
        val s2 = SubClass2()
        s2.superMethod2()
    
        println(" ------------------------- ")
    
        val s3 = SubClass3()
        s3.superMethod2()
    
        println(" ------------------------- ")
    
        val s4 = SubClass4(400)
        s4.superMethod2()
        
    }
    
    // 만일 부모 클래스에 대표생성자가 있다면?
    open class SuperClass2(val a1:Int){
        fun superMethod2(){
            println("superMethod2 실행 a1 = $a1")
        }
    }
    
    // 상속을 위해 부모클래스 이름을 쓰고 괄호를 연결했다면, 괄호안에 부모클래스의 대표생성자 매개변수에 맞게 전달인수를 전달해야 한다.
    // 그렇지 않으면 부모클래스의 멤버변수가 생성되지 않고, 상속도 안되는 에러가 발생한다.
    class SubClass2 : SuperClass2(200){
    
    }
    
    // 자식 클래스의 생성자가 따로 기술되어야 한다면 아래와 같이 표현한다.
    class SubClass3 : SuperClass2{
        constructor() : super(300){
        }
    }
    
    // 자식 클래스에도 대표생성자가 있다면
    class SubClass4 (var a4:Int) : SuperClass2(a4){
    
    }

     


     

    15. Override

    15.1. 일반적인 상속

    fun main(){
    
        // 상속이 구현된 자식 클래스를 이용하여 객체를 만들고 자식클래스가 새로 만든 변수를 출력하고 메서드를 실행해본다.
        var obj1:SubClass1 = SubClass1()
        println("obj1.subA1 : ${obj1.subA1}")  // obj1.subA1 : 200
        obj1.subMethod()                        // SubClass1의 subMethod1입니다
    
        // 상속받은 부모 클래스의 변수를 출력하고, 부모클래스에 있는 메서드를 실행
        println("obj1.superA1 : ${obj1.superA1}")   // obj1.superA1 : 100
        obj1.superMethod()                           // SuperClass1의 superMethod1입니다
    
    }
    
    // 일반적인 상속
    open class SuperClass1{ // 상속을 하기위해 클래스를 open을 사용하여 생성
        var superA1 = 100
        fun superMethod(){
            println("SuperClass1의 superMethod1입니다")
        }
    }
    
    // SuperClass1 클래스를 상속받는 SubClass1을 생성
    class SubClass1 : SuperClass1(){
        var subA1 = 200
        fun subMethod(){
            println("SubClass1의 subMethod1입니다")
        }
    }

     


     

    15.2. 오버라이딩을 위한 상속

    fun main(){
    
        val obj2:SubClass2 = SubClass2()
        // 오버라이딩 된 메서드를 실행
        obj2.superMethod2()     // SubClass2의 오버라이딩 된 superMethod2
    
        // 부모클래스의 레퍼런스 변수에 자식클래스의 인스턴스를 저장
        val obj3:SuperClass2 = SubClass2()
        // 부모클래스의 레퍼런스 변수로 실행한 메서드는 ... 자식 클래스에서 재정의된 메서드가 우선 실행된다.
        obj3.superMethod2()     // SubClass2의 오버라이딩 된 superMethod2
    
        println(" ----------------------------------------- " )
    
        // 전달인수로 부모 인스턴스 주소 전달
        Test1(obj2);    // SubClass2의 오버라이딩 된 superMethod2
        
        val obj4:SuperClass2 = SuperClass2()
        Test1(obj3)     // SubClass2의 오버라이딩 된 superMethod2
        Test1(obj4)     // SuperClass2의 superMethod2입니다
    
    }
    
    // 메서드 오버라이딩을 위한 상속
    open class SuperClass2{
        // 오버라이드가 될 부모클래스의 메서드는 반드시 앞에 open을 붙여 쓴다.
        open fun superMethod2(){
            println("SuperClass2의 superMethod2입니다")
        }
    }
    
    class SubClass2 : SuperClass2(){
        override fun superMethod2(){
            // super.superMethod2() // 부모 클래스의 오버라이딩 되기 전 메서드 호출 : super키워드 사용
            println("SubClass2의 오버라이딩 된 superMethod2")
        }
    }
    
    fun Test1(obj:SuperClass2){
        obj.superMethod2()
    }

     


     

    16. Any 

    • 자바의 'Object 클래스와 같은 것'
    • 모든 클래스의 부모 클래스 (최상위 클래스)
    • 이미 만들어져 있거나, 앞으로 생성될 클래스에 따로 명시하지 않아도, 다른 클래스를 상속받지 않아도, 그 클래스는 Any클래스를 상속받고 있다.
    • 다만 다른 클래스를 상속받고 있는 클래스는 그 부모 클래스가 Any클래스를 상속받고 있기 때문에 결과적으로 자기 자신도 Any클래스의 자식(손자) 클래스가 된다고 볼 수 있다.
    fun main(){
    
        val obj1 = TestClass11()
        println("obj1 : $obj1") // obj1 : days03.TestClass11@330bedb4
        // 참조변수를 println에 넣으면 Any클래스에서 상속받은 toString() 메서드가 실행되어 
        // 클래스 이름과 해시코드가 출력된다. obj1 : days03.TestClass11@해시코드
    
        // Any클래스에서 상속받은 toString메서드를 오버라이드 클래스는 재정의된 메서드가 실행된다.
        val obj2 = TestClass12()
        println("obj2 : $obj2") // obj2 : TestClass12 객체입니다.
    
        testFun(obj1)   // a:days03.TestClass11@330bedb4
        testFun(obj2)   // a:TestClass12 객체입니다.
    
    }
    
    // toString이 재정의되지 않은 클래스
    class TestClass11{ }
    
    class TestClass12{
        // Any클래스에서 상속받은 toString을 오버라이딩하여 사용할 수 있다.
        override fun toString() : String{
            return "TestClass12 객체입니다."
        }
    }
    
    // 매개변수의 자료형이 Any라는 것은 모든 클래스의 객체가 전달인수로 전달될 수 있다는 뜻
    fun testFun(a:Any){
        println("a:$a")
    }

     


    17. this / super

    17.1. this의 용도

    1. 멤버변수와 매개변수(또는 지역변수)의 이름이 같을 때, 그들을 구분하기 위해 사용한다.
    2. 대표생성자가 있는 경우 보조생성자에서 기본생성자의 매개변수 개수와 자료형에 맞춰서 반드시 호출해야하는데, 이때 this 키워드를 사용한다.
    3. 메서드 내부에 다른 메서드(이름하야 지역메서드:지역변수와 같은 의미)를 만들어 쓸 수 있는데 멤버메서드와 구분하기 위해 사용한다.
    fun main(){
    
        var obj1 = TestClass21()
        obj1.testMethod1()
    
    }
    
    // ** this의 용도 **
    // 멤버변수와 매개변수(또는 지역변수)의 이름이 같을 때 그들을 구분하기 위해 사용한다.
    // 대표 생성자가 있는 경우 보조 생성자에서 기본 생성자의 매개변수 개수와 자료형에 맞춰서 반드시 호출해야하는데, 이때 this 키워드를 사용한다.
    // 메서드 내부에 다른 메서드 (이름하야 지역메서드:지역변수와 같은 의미)를 만들어 쓸 수 있는데 멤버메서드와 구분하기 위해 사용한다.
    
    class TestClass21 (var a2:Int){
        var a1:Int = 200
        constructor():this(300){
            // 보조생성자는 반드시 대표생성자를 this키워드를 이용하여 호출해야한다. 대표생성자의 매개변수가 멤버변수로 생성되어야 하는데,
            // 호출되지 않으면 생성되지 않기 떄문이다.
        }
        fun testMethod1(){
            var a1 = 100
            println("메서드의 지역변수 a1 : $a1")
            println("TestClass21의 멤버변수 a1 : ${this.a1}")
            // $this.a1이라고 쓰면, 클래스명@해시코드.a1으로 출력된다.
            // 멤버변수의 값을 출력하고 싶다면, 위와같이 ${this.a1}라고 작성한다.
            fun testMethod2(){
                println("testMethod1() 내부의 testMethod2() 메서드")
            }
            testMethod2()   // 지역메서드 우선 실행
            this.testMethod2()  // 멤버메서드 실행
        }
        fun testMethod2(){
            println("멤버메서드 testMethod2()")
        }
    }

     


     

    17.2. super의 용도

    1. 상속관계의 자식 생성자에서, 부모클래스의 생성자를 호출할 때, 자식 클래스의 이름 옆에 부모 클래스의 이름을 사용하지만, 보조 생성자에서는 super라는 키워드를 쓴다.
    2. 자식 클래스에서 오버라이드된 메서드 그리고 변수를 사용할 때, 인위적으로 부모의 메서드를 호추하거나 변수값을 취할 때 사용한다.
    fun main(){
    
        var obj2 = SubClass11()
        obj2.superMethod1()
        //SuperClass의 superMethod1()
        //super.a1 : 100
        //SubClass의 superMethod1
    
        println(" ------------- ")
    
        var obj3 = SubClass12(600)
        obj2.superMethod1()
        //SuperClass의 superMethod1()
        //super.a1 : 100
        //SubClass의 superMethod1
    
    }
    
    // ** super의 용도 **
    // 상속관계의 자식 생성자에서, 부모클래스의 생성자를 호출할 때, 자식 클래스의 이름 옆에 부모 클래스의 이름을 사용하지만, 보조 생성자에서는 super라는 키워드를 쓴다.
    // 자식 클래스에서 오버라이드된 메서드 그리고 변수를 사용할 때, 인위적으로 부모의 메서드를 호추하거나 변수값을 취할 때 사용한다.
    
    open class SuperClass(var a2:Int){  //대표생성자
        open var a1 = 100
        constructor():this(200){ }  // 보조생성자
        open fun superMethod1(){
            println("SuperClass의 superMethod1()")
        }
    }
    
    // 대표생성자와 보조생성자가 모두 있는 부모클래스는 둘 중 어떤 형태로도 상속(부모클래스 생성자호출)이 가능하다.
    class SubClass11 : SuperClass(/*200*/) {
        override fun superMethod1() {
            super.superMethod1()    // 부모클래스의 메서드
            println("super.a1 : ${super.a1}")   // 부모클래스의 멤버변수
            println("SubClass의 superMethod1")
        }
    }
    
    class SubClass12 : SuperClass {
        // constructor키워드로 생성된 생성자 이름 옆에 super키워드를 이용하여 부모클래스의 대표생성자를 직접 호출할 수 있다.
        // 그 경우 Extends로 연결되는 부모클래스 이름 옆의 괄호는 지워진다.
        // class SubClass12 : SuperClass() -> class SubClass12 : SuperClass
        constructor(a1:Int) : super(a1){ }
    }

     


     

    18. Abstract 추상클래스

    fun main(){
    
        val obj1 = Sub1()
        obj1.method1()  // Super1의 method1이다.
        obj1.method2()  // 재정의된 Super1의 method2이다.
    
        println(" ----------------------------- ")
    
        val obj2 = Sub2()
        testFun1(obj2)
        // Super1의 method1이다.
        // 재정의된 Super1의 method2이다.
    
        testFun1(obj1)
        // Super1의 method1이다.
        // 재정의된 Super1의 method2이다.
    
    }
    
    // 부모클래스의 참조변수가 멤버변수로 있기때문에, 그 자식클래스의 객체를 전달인수로 전달할 수 있다.
    fun testFun1(obj1:Super1){
        obj1.method1()
        obj1.method2()
    }
    
    
    
    // 추상클래스 : 상속을 위한 클래스, 멤버메서드의 override에 강제성을 부여하기 위해 사용한다.
    open abstract class Super1{
    
        // 재정의(override)의 강제성이 있는 메서드와 없는 메서드를 동시에 사용이 가능하다.
        fun method1(){
            println("Super1의 method1이다.")
        }
        open abstract fun method2() // 재정의(override) 강제성이 부여된 추상메서드
    }
    
    
    class Sub1:Super1(){
        override fun method2() {
            println("재정의된 Super1의 method2이다.")
        }
    }
    
    class Sub2:Super1(){
        override fun method2() {
            println("재정의된 Super1의 method2이다.")
        }
    }

     


     

    19. Interface

    • 인터페이스 : 추상클래스가 할 수 없던, '다중상속'이 가능하다.
    • 자바의 버전이 업그레이드 되면서,
      '자바의 인터페이스에도 override의 강제성이 부여되지 않는 일반변수와 메서드 선언이 가능'해졌다.
    fun main(){
    
        var obj31 = TestClass31()    // Inter1을 상속(구현)받은 자식클래스
        obj31.inter1_Method1()  // Inter1의 Method1이다.
        obj31.inter1_Method2()  // TestClass31의 Inter1에서 오버라이딩된 Method2이다.
    
        println(" ------------------ ")
    
        var obj32 = TestClass32()    // Inter2를 상속(구현)받은 자식클래스
        obj32.inter2_Method1()  // Inter2의 Method1이다.
        obj32.inter2_Method2()  // TestClass32의 Inter2에서 오버라이딩된 Method2이다.
    
    }
    
    
    // 인터페이스 : 추상클래스가 할 수 없던, '다중상속'이 가능하다.
    // 자바의 버전이 업그레이드 되면서, '인터페이스에도 override의 강제성이 부여되지 않는 일반변수와 메서드 선언이 가능'해졌다.
    
    interface Inter1{
        fun inter1_Method1(){
            println("Inter1의 Method1이다.")
        }   // override의 강제성이 부여되지 않는 메서드
        
        fun inter1_Method2() // override의 강제성이 부여된 메서드
    }
    
    interface Inter2 {
        fun inter2_Method1() {
            println("Inter2의 Method1이다.")
        }
        
        fun inter2_Method2()
    }
    
    
    // 인터페이스의 구현은 자식클래스명 옆에 ":"와 함께 인터페이스명만 기술한다.
    class TestClass31:Inter1{
        override fun inter1_Method2() {
            println("TestClass31의 Inter1에서 오버라이딩된 Method2이다.")
        }
    }
    
    class TestClass32 : Inter2{
        override fun inter2_Method2() {
            println("TestClass32의 Inter2에서 오버라이딩된 Method2이다.")
        }
    }

     


     

    19.1. 다중 상속

    fun main(){
    
        // 다중상속
        var obj33 = TestClass33()    // Inter1과 Inter2를 상속(구현)받은 자식클래스
        obj33.inter1_Method1()  // Inter1의 Method1이다.
        obj33.inter1_Method2()  // TestClass33의 Inter1에서 오버라이딩된 Method2이다.
        obj33.inter2_Method1()  // Inter2의 Method1이다.
        obj33.inter2_Method2()  // TestClass33의 Inter2에서 오버라이딩된 Method2이다.
    
        println(" ------------------ ")
    
        // 부모중 해당 인터페이스가 구현되어있는 객체만 전달인수로 사용이 가능합니다.
        
        // obj32는 test1에 전달이 불가능하다.
        // testFun1(obj32)
    
        // obj31는 전달이 불가능하다.
        // testFun2(obj31)
    
        // obj33(TestClass33)은 Inter1이 구현되어있어서 인수로 전달이 가능하다.
        testFun1(obj33)
        // Inter1의 Method1이다.
        // TestClass33의 Inter1에서 오버라이딩된 Method2이다.
    
        // obj33(TestClass33)은 Inter2가 구현되어있어서 인수로 전달이 가능하다.
        testFun2(obj33)
        // Inter2의 Method1이다.
        // TestClass33의 Inter2에서 오버라이딩된 Method2이다.
    
    
    }
    
    
    interface Inter1{
        fun inter1_Method1(){
            println("Inter1의 Method1이다.")
        }   // override의 강제성이 부여되지 않는 메서드
    
        fun inter1_Method2() // override의 강제성이 부여된 메서드
    }
    
    interface Inter2 {
        fun inter2_Method1() {
            println("Inter2의 Method1이다.")
        }
    
        fun inter2_Method2()
    }
    
    
    // 다중상속 클래스
    // 두 개 이상의 인터페이스를 구현하려면 인터페이스명을 ','로 구분하여 기술한다.
    class TestClass33 : Inter1, Inter2{
        override fun inter1_Method2() {
            println("TestClass33의 Inter1에서 오버라이딩된 Method2이다.")
        }
        override fun inter2_Method2() {
            println("TestClass33의 Inter2에서 오버라이딩된 Method2이다.")
        }
    }
    
    
    fun testFun1(obj:Inter1){
        obj.inter1_Method1()
        obj.inter1_Method2()
    }
    
    fun testFun2(obj:Inter2){
        obj.inter2_Method1()
        obj.inter2_Method2()
    }

     


    20. Companion

    • 자바에서 'static'으로 사용되던 키워드가 'companion'이라는 키워드로 바뀌어 사용된다.
    • 다만, 'static'은 각 변수나 메서드에 각각 붙여 사용하지만,
      'companion'영역을 만들고 그 안에 정적멤버로 사용할 변수나 메서드를 다 때려 넣는다.
    • companion 영역에 선언된 멤버들은 객체 생성없이, 클래스 이름과 함께 바로 사용이 가능하다.
    • 인스턴스 멤버는 companion(static)메서드에 접근할 수 없다.
    • 정적 멤버 메서드에서는 정적멤버변수의 접근이 가능
    fun main(){
    
        var obj1 = TestClass()
        println("obj1.a1 : ${obj1.a1}") // obj1.a1 : 100
    
        obj1.testFun1()
        // 인스턴스 메서드 testFun1 실행!
        // a1:100
        // a2:200
        // companion메서드 testFun2 실행!
        // a2:200
    
        obj1.a1 = 200
        println("obj1.a1 : ${obj1.a1}") // obj1.a1 : 200
    
        println(" -------------------- ")
    
        // companion 영역에 선언된 멤버들은 객체 생성없이, 클래스 이름과 함께 바로 사용이 가능하다.
        println("TestClass.a2 : ${TestClass.a2}")   // TestClass.a2 : 200
        TestClass.testFun2()
        // companion메서드 testFun2 실행!
        // a2:200
    
    }
    
    class TestClass{
        var a1 = 100    // 인스턴스 멤버 변수
    
        companion object{
            var a2 = 200
            fun testFun2(){
                println("companion메서드 testFun2 실행!")
                //println("a1:$a1") // 에러발생 - 인스턴스 멤버는 companion(static)메서드에 접근할 수 없다.
                println("a2:$a2")   // 정적 멤버 메서드에서는 정적멤버변수의 접근이 가능
            }
        }
    
        fun testFun1(){ // 인스턴스 멤버 메서드
            println("인스턴스 메서드 testFun1 실행!")
            println("a1:$a1")
            println("a2:$a2")   // 인스턴스 메서드는 companion변수(a2)를 언제든 사용이 가능하다.
            testFun2()
        }
    }

     


     

    21. 형변환 ( type casting )

    21.1. 자료형변환

    var a1:Int = 100
    // var a2:Double = a1
    var a2:Double = a1.toDouble()
    
    var b1:Double = 123.12
    // var b2:Int = b1
    var b2:Int = b1.toInt()
    
    var c1:String = "12345"
    var c2:Int = c1.toInt()
    
    var d1:String = "123.45"
    var d2:Double = d1.toDouble()
    
    // var d3:String = "안녕하세요"
    // var number4:Int = d3.toInt() // 문자 -> 정수 : 형변환 불가능

     


     

    21.2. 스마트 캐스트 ( 플로우 기반 형변환)

        // ** 객체 타입 검사 **
        // 위 두개의 경우는 형변환 전에 안전한 실행을 위해 형변환이 가능한지에 대한 점검이 필요하다.
    
        // 자바에서는
        // if(super1 instanceof subClass41){
        //      val sub1:subClass41 = (SubClass41)super1    //형변환 명령
        // }
    
        // 코틀린에서는
        // if(super1 is SubClass41){
        //      super1 as SubClass41    //코틀린 강제 형변환 명령
        // }
    
        val super2:SuperClass4 = SubClass41()
        val inter2:Inter4 = SubClass42()
        // 부모레퍼런스가 저장하고 있는 자식인스턴스를 자식레퍼런스에 옮길텐데,
        // 위에서 언급한 것처럼 코틀린은 옮기지 않고, 레퍼런스 변수 자체를 변경한다.
        // 그 전에 변경이 가능한지 검사
        if(super2 is SubClass41){
            super2 as SubClass41    // if문의 중괄호 밖에서도 형변환 결과를 활용할 수 있다.
        } else {
            println("형변환이 불가능하다.")
        }   // 검사결과가 참이면 변환, 거짓이면 변환하지 않음
    
        if(inter2 is SubClass42) {
            // is에 의해 열린 if문 안에서는 as를 쓰지 않아도
            // is연산자 값이 true라면 형변환이 가능한 상황이므로 스마트 캐스팅 실행
            // 현재위치 중괄호{}안에서는 이미 inter2는 SubClass42의 레퍼런스로 변경된 상태이며, 그 상태로 사용이 가능하다.
            // 다만 중괄호를 벗어나면 다시 Inter2(부모인터페이스)의 레퍼런스로 되돌아간다.
            // 중괄호가 끝나도 계속 변경상태를 유지하려면 inter4 as SubClass42를 사용한다.
        }
        
    }
    
    open class SuperClass4
    interface Inter4
    class SubClass41 : SuperClass4(){
        fun subMethod1(){
            println("SubClass41의 subMethod1입니다")
        }
    }
    class SubClass42 : Inter4{
        fun subMethod2(){
            println("SubClass41의 subMethod2입니다")
        }
    }

     


     

    22. List

    • 배열과 동일하게 인덱스 번호를 통해 객체를 관리한다.
    • 배열의 크기가 정해지면 변경할 수 없지만 List는 추가, 삭제 등이 가능하다.

    <코틀린의 컬렉션>

    List 순서를 통해 관리한다.
    Map 이름을 통해 관리한다.
    Set 집합 관리

     

    22.1. 리스트 생성

    List 생성 메서드
    listOf( ) 불변형 리스트 생성
    mutableListOf( ) 가변형 리스트 생성
    emptyList( ) 비어있는 불변형 리스트를 생성
    lsitOfNotNull( ) null을 제외한 나머지만으로 리스트를 생성한다.
    val list1 = listOf(10, 20, 30, 40, 50)
    val list2 = listOf("문자열1", "문자열2", "문자열3")
    println("list1:$list1") // list1:[10, 20, 30, 40, 50]
    println("list2:$list2") // list2:[문자열1, 문자열2, 문자열3]
    
    var list3 = mutableListOf<Int>()    // 가변형이기 때문에 더러는 비어있는 상태로 시작되고 요소들을 추가하여 변경한다.
    var list4 = mutableListOf("문자열1", "문자열2", "문자열3")
    println("list3:$list3") // list3:[]
    println("list4:$list4") // list4:[문자열1, 문자열2, 문자열3]
    
    val list5 = emptyList<Int>()
    println("list5:$list5") // list5:[]
    
    // null을 제외한 나머지들로 리스트를 구성
    val list6 = listOfNotNull(10, 20, null, 30, null, 40, 50)
    println("list6:$list6") // list6:[10, 20, 30, 40, 50]
    
    for (item in list1) {
        print("$item ")
    }   // 10 20 30 40 50
    println("\nlist1 size : ${list1.size}") // list1 size : 5

     


    22.2. 리스트 요소에 접근

    get( ) 0부터 시작하는 인덱스 번호를 통해 객체를 추출할 수 있다.
    // 리스트 요소에 접근
    // get : 0 부터 시작하는 인덱스 번호를 통해 객체를 추출할 수 있다.
    // kotlin의 리스트는 배열과 동일하게 []를 사용할 수 있다.
    println("list1 0 : ${list1.get(0)}")    // list1 0 : 10
    println("list1 1 : ${list1.get(1)}")    // list1 1 : 20
    println("list1 2 : ${list1[2]}")    // list1 2 : 30
    println("list1 3 : ${list1[3]}")    // list1 3 : 40

     


     

    22.3. 리스트 기타 메서드

    indexOf( )  지정된 값의 인덱스 번호를 반환한다. 값이 없을 경우 -1을 반환
    lastIndexOf( ) 지정된 값이 마지막에 해당하는 값의 인덱스 번호를 반환한다. 값이 없을경우 -1을 반환
    subList( ) 지정된 범위의 값을 추출하여 새로운 리스트를 생성해 반환한다.
    // 기타 메서드
    // indexOf : 지정된 값의 인덱스 번호를 반환한다. 값이 없을 경우 -1을 반환
    // lastIndexOf : 지정된 값이 마지막에 해당하는 값의 인덱스 번호를 반환한다. 값이 없을경우 -1을 반환
    // subList : 지정된 범위의 값을 추출하여 새로운 리스트를 생성해 반환한다.
    val list7 = listOf(10, 20, 30, 10, 20, 30)
    val index1 = list7.indexOf(20)
    println("index1 : $index1") // index1 : 1
    val index2 = list7.lastIndexOf(20)
    println("index2 : $index2") // index2 : 4
    val list8 = list1.subList(1, 3)
    println("list8 : $list8")   // list8 : [20, 30]

     


     

    22.4. 가변형 리스트 메서드

    add(객체) 객체를 추가한다.
    add(인덱스, 객체)  주어진 인덱스 위치에 객체를 삽입한다.
    remove(객체) 주어진 객체를 제거한다
    removeAt(인덱스) 주어진 인덱스의 객체를 제거한다.
    set(인덱스, 객체) 주어진 인덱스에 객체를 덮어씌운다.
    // 가변형 리스트의 메서드
    
    list3.add(10)
    list3.add(20)
    list3.add(30)   // 가변 리스트에 요소를 하나씩 추가한다.
    list3.addAll(listOf(40, 50, 60))  // 가변 리스트에 요소를 한번에 여러개 추가한다.
    println("list3 : $list3")   // list3 : [10, 20, 30, 40, 50, 60]
    
    list3.add(1, 100)   // 특정인덱스에 데이터를 추가한다. 뒤에 요소를 한칸씩 밀린다.
    println("list3 : $list3")   // list3 : [10, 100, 20, 30, 40, 50, 60]
    
    // 특정 인덱스에 데이터를 여러개 한번에 추가한다. 뒤에 요소를 한칸씩 밀린다.
    list3.addAll(2, listOf(200,300,400))
    println("list3 : $list3")   // list3 : [10, 100, 200, 300, 400, 20, 30, 40, 50, 60]
    
    // 리스트 요소중 해당값을 한개 또는 여러개 제거
    list3.remove(100)
    println("list3 : $list3")   // list3 : [10, 200, 300, 400, 20, 30, 40, 50, 60]
    
    list3.removeAll(listOf(200,300))
    println("list3 : $list3")   // list3 : [10, 400, 20, 30, 40, 50, 60]
    
    list3.removeAt(1)   //지정한 인덱스의 요소를 삭제
    println("list3 : $list3")   // list3 : [10, 20, 30, 40, 50, 60]
    
    list3.set(1,200)    // 지정한 인덱스에 지정한 값으로 수정한다.
    println("list3 : $list3")   // list3 : [10, 200, 30, 40, 50, 60]
    
    list3[2] = 300  // 배열처럼 지정한 인덱스에 지정한 값으로 수정
    println("list3 : $list3")   // list3 : [10, 200, 300, 40, 50, 60]

     


     

    22.5. 불변형 리스트를 가변형으로 변환

    // 불변형 리스트(list1)를 가변형으로 변환
    // list1.add(1000)  // 불변형이기 떄문에 요소 추가는 에러
    val list100 = list1.toMutableList()     // 불변형 -> 가변형 : 불변형의 변환결과를 다른 참조변수에 전달
    list100.add(1000)
    println("list100 : $list100")   // list100 : [10, 20, 30, 40, 50, 1000]
    
    //val list200 = list100.to
    //list200.add(300) // 에러

     

    300x250