[TECHIT] 코틀린 4

hegleB·2023년 5월 25일
post-thumbnail

Any

코틀린에서 모든 클래스가 직접 또는 간접적으로 상속받는 클래스로 상속에 대한 코드를 작성하지 않으면 Any 클래스로 상속 받는다. 모든 클래스는 Any를 상속 받기 때문에 모든 객체는 Any타입의 변수를 담을 수 있다.

Any 클래스가 제공하는 overriding 메서드 toString, equals 등이 있다.

    override fun toString(): String {
        println("a1 : $a1")
        println("a2 : $a2")

        return super.toString()
    }
  • toString : 객체의 정보 문자열을 반환하기 위해 만드는 메서드
    override fun equals(other: Any?): Boolean {
        if(other != null){
            // 형변환
            val temp = other as TestClass3
            if(this.a1 == temp.a1 && this.a2 == temp.a2){
                return true
            } else {
                return false
            }
        }
        return false
    }
  • equals : == 연산자를 사용할 시 자동으로 호출되며 우측에 있는 객체가 매개변수로 들어온다. 이 메서드가 반환하는 결과가 == 연산의 결과가 된다.

추상클래스

  • 추상메서드를 가지고 있는 클래스

추상메서드

  • 구현되지 않은 메서드

[Kotlin]

fun main(){
    val t2 = TestClass2()
    t2.testMethod()

    val t3:TestClass1 = TestClass2()
    t3.testMethod()
}

// 추상 클래스 정의한다.
open abstract class TestClass1{

    // 추상 메서드
    open abstract fun testMethod()
}

// 추상 클래스를 상속 받은 클래스
class TestClass2 : TestClass1(){
    override fun testMethod() {
        println("TestClass2의 testMethod")
    }
}

[Java]

추상 클래스는 객체를 생성할 수 없으며 추상 클래스를 상속 받은 클래스는 추상메서드를 overriding이 된다. abstract 키워드를 사용하여 상속이 가능하다. 이떄 open을 선언해야한다. 이 키워드를 사용하지 않으면 자바 코드로 봤을 떄 final로 선언이 되어 상속이 불가능해진다. 따라서 open 키워드를 사용하여 상속이 가능하도록 해야한다.~~
추상 클래스메서드 오버라이딩에 대한 강제성을 주기 위한 목적을 가지고 있다.

예제 문제

중국집
메뉴를 선택해주세요
1. 짜장면, 2. 짬뽕, 3. 볶음밥, 4. 종료
주문 총 금액 : 30000원
짜장면 : 0개
짬뽕 : 0개
볶음밥 : 0개
음식 : 짜장면
가격 : 6000원
곱배기여부 : 곱배기 입니다 or 곱배기 아닙니다
음식 : 짬뽕
가격 : 8000원
홍합여부 : 홍합이 있습니다 or 홍합이 없습니다
음식 : 볶음밥
가격 : 10000원
국물종류 : 짬뽕국물 or 계란국물
각 클래스를 통해 생성된 객체를 담을 ArrayList는 하나만 만들어준다.
주문내역 출력시 주문한 음식 순서대로 출력한다.
프로젝트의 이름은 Kotlin15_InheritEX1 로 해주세요

fun main() {
    val chinessRestaurant = ChinessRestaurant()
    chinessRestaurant.selectMenu()
    chinessRestaurant.printMenu()
    chinessRestaurant.printFoodList()
}

class ChinessRestaurant {
    val scanner = Scanner(System.`in`)

    // 음식들을 담을 리스트
    val foodList = ArrayList<Food>()

    // 메뉴를 선택하는 기능
    fun selectMenu() {
        var selectNumber = 0

        while (true) {
            println("메뉴를 선택해주세요")
            print("1. 짜장면, 2. 짬뽕, 3. 볶음밥, 4. 종료 : ")
            selectNumber = scanner.nextInt()

            if (selectNumber !in 1..4) {
                println("번호를 다시 입력해주세요")
                continue
            }
            if (selectNumber == 4) {
                break
            }

            when (selectNumber) {
                1 -> {
                    print("곱배기 인가요 ?(1. 곱배기O, 2. 곱배기X) : ")
                    val temp = scanner.nextInt()
                    var temp2 = true
                    if (temp == 2) {
                        temp2 = false
                    }
                    val food = JJaJangMyun("짜장면", 6000, temp2)
                    foodList.add(food)
                }

                2 -> {
                    print("홍합이 있나요? (1. 있음, 2. 없음) : ")
                    val temp = scanner.nextInt()
                    var temp2 = true
                    if (temp == 2) {
                        temp2 = false
                    }
                    val food = JJamPPong("짬뽕", 8000, temp2)
                    foodList.add(food)
                }

                3 -> {
                    print("국물 종류가 무엇인가요? (1. 짬뽕국물, 2.계란국물) : ")
                    val temp = scanner.nextInt()
                    var temp2 = "짬뽕국물"
                    if (temp == 2) {
                        temp2 = "계란국물"
                    }
                    val food = BockUmBab("볶음밥", 10000, temp2)
                    foodList.add(food)
                }
            }
        }
    }

    // 주문 내역을 출력하는 기능
    fun printMenu() {
        // 총 주문 금액
        var totalPrice = 0

        // 각 음식의 개수
        var jjaJangMyunCount = 0
        var jjamPpongCount = 0
        var bockUmBabCount = 0

        // 짜장면 금액 누적
        for (food in foodList) {
            totalPrice += food.price

            when(food.name){
                "짜장면" -> jjaJangMyunCount++
                "짬뽕" -> jjamPpongCount++
                "볶음밥" -> bockUmBabCount++
            }
        }

        println("주문 총 금액 : ${totalPrice}원")
        println("짜장면 : ${jjaJangMyunCount}개")
        println("짬뽕 : ${jjamPpongCount}개")
        println("볶음밥 : ${bockUmBabCount}개")
        println()
    }

    // 주문한 음식들을 출력하는 기능
    fun printFoodList() {
        for (food in foodList) {
            food.printFoodInfo()
        }
    }

}

// 위에서 printFoodList 메서드에서 자식클래스에 오버라이딩한 printFoodInfo
// 를 호출할 것이므로 메서드 오버라이딩에 대한 강제성을 주기위해
// 추상클래스와 추상 메서드로 정의한다.
open abstract class Food (var name:String, var price:Int) {
    open abstract fun printFoodInfo()
}

class JJaJangMyun : Food {

    var isDouble:Boolean = false

    constructor(name:String, price:Int, isDouble:Boolean) : super(name, price) {
        this.isDouble = isDouble
    }


    override fun printFoodInfo(){
        println("음식 : $name")
        println("가격 : ${price}원")

        if(isDouble == true){
            println("곱배기 여부 : 곱배기 입니다")
        } else {
            println("곱배기 여부 : 곱배기 아닙니다")
        }
    }
}

class JJamPPong : Food {

    var hasMuseel:Boolean = false

    constructor(name:String, price:Int, hasMuseel:Boolean) : super(name, price){
        this.hasMuseel = hasMuseel
    }

    override fun printFoodInfo(){
        println("음식 : $name")
        println("가격 : ${price}원")

        if(hasMuseel == true){
            println("홍합 여부 : 홍합이 있습니다")
        } else {
            println("홍합 여부 : 홍합이 없습니다")
        }
    }
}

class BockUmBab : Food {

    lateinit var soupType:String

    constructor (name:String, price:Int, soupType:String) : super(name, price){
        this.soupType = soupType
    }


    override fun printFoodInfo(){
        println("음식 : $name")
        println("가격 : ${price}원")
        println("국물 종류 : $soupType")
    }
}

인터페이스

코틀린은 다중 상속을 지원하지 않다. 따라서 객체를 자기 타입의 변수나 부모형 타입 변수에만 할당할 수 있다. 하지만 다양한 타입의 변수에 객체의 주소 값을 할당하고 싶을 때 인터페이스를 사용할 수 있다.
인터페이스는 클래스가 아니므로 객체를 직접 생성할 수 없다. 그러나 클래스는 한 개 이상의 인터페이스를 구현할 수 있으며, 구현한 인터페이스 형식으로 생성된 객체를 참조할 수 있다. 또한 추상 메서드와 일반 메서드를 모두 구현할 수 있다. 추상 클래스와 유사한 목적을 가지지만, 클래스는 여러 인터페이스를 구현할 수 있는 장점이 있다.

예제 문제

동물원
동물사육장, 해양생물놀이터, 방목장, 밀림
동물 사육장은 모든 동물들이 있는 곳
해양생물놀이터는 돌고래와 상어가 있는 곳
방목장은 말과 기린이 있는 곳
밀림은 호랑이와 사자가 있는 곳
동물원에 있는 모든 동물들은 동물 사육장에 넣어주면 모든 동물들이 밥을 먹는다.
동물원에 있는 모든 동물들을 해양생물 놀이터에 넣어주면 돌고래와 상어가 헤엄을 친다.
동물원에 있는 모든 동물들을 방목장에 넣어주면 말과 기린과 호랑이만 뛰어 다닌다.
동물원에 있는 모든 몽물들을 밀림에 넣어주면 상어와 기린과 사자가 사냥을 한다.
돌고래는 밥을 먹을 때 "냠냠냠"하고 먹는다.
상어는 밥을 먹을 때 "얌얌얌"하고 먹는다.
말은 밥을 먹을 때 "당근 당근"하고 먹는다.
기린은 밥을 먹을 때 "풀풀풀" 하고 먹는다.
호랑이가 밥을 먹을 때 "아작 아작"하고 먹는다.
사자가 법을 먹을 때 "꿀꺽 꿀꺽" 하고 는다.
돌고래는 헤엄을 칠 때 "돌~돌~"하면서 헤엄을 친다.
상어가 헤엄을 칠 때 "슈웅 슈융" 하면서 헤엄을 친다.
말이 뛰어 다닐 때닌 "이히히히힝~" 하면서 뛰어 다니고
기린이 뛰어 다닐 때는 "영차~ 영차~" 하면서 뛰어 다닌다.
호랑이가 뛰어 다닐 때는 "헥~ 헥~" 하면서 뛰어 다닌다.
상어가 사냥을 하면 "으아아아아아!!!" 하면서 사냥을 한다.
기린이 사냥을 하면 "가즈아~" 하고 사냥을 한다.
사자가 사냥을 하면 "암컷아 사냥해와라~"하고 사냥을 한다.
프로그램이 시작되면 다음과 같이 입력을 받는다.
어떤 동물을 동물원에 넣어줄까요?
1. 돌고래, 2. 상어, 3. 말, 4. 기린, 5. 호랑이, 6. 사자, 0. 그만넣어 :
번호를 잘못 입력하면 잘못 입력하였습니다를 보여준다.
0을 입력하면 입력을 중단한다.
모든 동물들을 사육장에 넣어 결과를 출력한다.
모든 동물들을 해양생물 놀이터에 넣어 결과를 출력한다.
모든 동물들을 방목장에 넣어 결과를 출력한다.
모든 동물들을 밀림에 넣어 결과를 출력한다.
밥먹는 것, 헤엄치는 것, 뛰어 다는 것, 사냥하는 것은 모두 메서드로 만들어준다.
--- 출력 ---
타입 : 돌고래
크기 : 돌고래 만큼 크다
헤엄 속도 : 300노트
타입 : 상어
크기 : 상어 만큼 크다
헤엄 속도 : 500노트
타입 : 말
크기 : 말 만큼 크다
달리기 속도 : 300km/h
타입 : 기린
크기 : 기린 만큼 크다
달리기 속도 : 500km/h
타입 : 호랑이
크기 : 호랑이 만큼 크다
먹이량 : 500마리
타입 : 사자
크기 : 사자만큼 크다
먹이량 : 600마리
돌고래는 밥을 먹을 때 "냠냠냠"하고 먹는다.
상어는 밥을 먹을 때 "얌얌얌"하고 먹는다.
말은 밥을 먹을 때 "당근 당근"하고 먹는다.
기린은 밥을 먹을 때 "풀풀풀" 하고 먹는다.
호랑이가 밥을 먹을 때 "아작 아작"하고 먹는다.
사자가 법을 먹을 때 "꿀꺽 꿀꺽" 하고 는다.
돌고래는 헤엄을 칠 때 "돌~돌~"하면서 헤엄을 친다.
상어가 헤엄을 칠 때 "슈웅 슈융" 하면서 헤엄을 친다.
말이 뛰어 다닐 때닌 "이히히히힝~" 하면서 뛰어 다니고
기린이 뛰어 다닐 때는 "영차~ 영차~" 하면서 뛰어 다닌다.
호랑이가 뛰어 다닐 때는 "헥~ 헥~" 하면서 뛰어 다닌다.
상어가 사냥을 하면 "으아아아아아!!!" 하면서 사냥을 한다.
기린이 사냥을 하면 "가즈아~" 하고 사냥을 한다.
사자가 사냥을 하면 "암컷아 사냥해와라~"하고 사냥을 한다.
모든 출력은 항상 입력한 순서대로 출력한다.

[설계방법]
1. 어떠한 클래스들이 필요한지를 정리한다(한국어 주석)
2. 각 클래스들의 이름을 정의한다(class 클래스명)
3. 각 클래스가 가져야할 모든 변수와 모든 메서드들을 클래스 내부에 정의한다(한국어 주석)
4. 한국어 주석으로 작성한 모든 변수와 모든 메서드들을 작성한다(멤버변수, 메서드는 { } 까지만)
5. 각 클래스 별로 중복되는 요소들을 정리한다.
6. 중복되는 요소들을 가지는 부모 클래스들을 정의한다.
7. 정의된 부모 클래스들 중에 메서드로만 구성되는 것은 인터페이스로 변경한다.
8. 부모 클래스들이 상속 받거나 구현해야 하는 인터페이스들을 설정해준다.
8-1. 만약 자식 클래스가 구현해야 하는 메서드가 있다면 추상 메서드로 정의하고 클래스는 추상 클래스로 정의한다.
9. 최종 자식 클래스에 상속과 인터페이스를 설정해준다.

import java.util.Scanner

fun main(){
   val zoo = Zoo()
   zoo.inputAnimalNumber()
   zoo.animalAction()
}

// 동물원
class Zoo{

   val scanner = Scanner(System.`in`)
   val animalList = ArrayList<Animal>()

   // 동물의 타입을 입력받는 메서드
   fun inputAnimalNumber(){
       var inputNumber = 0

       while(true){
           println("어떤 동물을 동물원에 넣어줄까요?")
           print("1. 돌고래, 2. 상어, 3. 말, 4. 기린, 5. 호랑이, 6. 사자, 0. 그만넣어 : ")
           inputNumber = scanner.nextInt()

           if(inputNumber !in 0..6){
               println("잘못 입력하였습니다")
               continue
           }

           if(inputNumber == 0){
               break
           }

           // 동물 객체를 생성한다.
           val animal = createAnimal(inputNumber)
           animalList.add(animal)
       }
   }

   // 동물 객체를 만드는 메서드
   fun createAnimal(type:Int) = when(type){
       1 -> Dolphin("돌고래", "돌고래 만큼 크다", 300)
       2 -> Shark("상어", "상어 만큼 크다", 500)
       3 -> Horse("말", "말 만큼 크다", 300)
       4 -> Giraffe("기린", "기린 만큼 크다", 500)
       5 -> Tiger("호랑이", "호랑이 만큼 크다", 500)
       6 -> Lion("사자", "사자만큼 크다", 600)
       else -> Lion("하하하", "호호호", 0)
   }

   // 각 동물들의 기능을 동작시키는 메서드
   fun animalAction(){
       val vivarium = Vivarium()
       val marineLifePlayGround = MarineLifePlayGround()
       val pasture = Pasture();
       val jungle = Jungle();

       for(animal in animalList){
           animal.printInfo()
           println()
       }

       vivarium.fee(animalList)
       println()
       marineLifePlayGround.swimming(animalList)
       println()
       pasture.run(animalList)
       println()
       jungle.hunt(animalList)
       println()
   }
}

// 동물 사육장
class Vivarium {
   // - 동물들이 밥을 먹는 기능
   fun fee(animalList:ArrayList<Animal>){
       for(animal in animalList){
           animal.eat()
       }
   }
}

// 해양 생물 놀이터
class MarineLifePlayGround{
   // - 동물들이 헤엄을 치는 기능
   fun swimming(animalList: ArrayList<Animal>){
       for(animal in animalList){
//            if(animal.type == "돌고래" || animal.type == "상어"){
//                val temp = animal as MarinAnimal
//                temp.swimming()
//            }
           // 객체에 MarinAnimal 부분이 있다면 (상속을 받았거나 인터페이스를 구현했다면...
           // true가 반환되고 객체를 MarinAnimal 타입으로 형변환까지 해준다.
           if(animal is MarinAnimal){
               animal.swimming()
           }
       }
   }
}

// 방목장
class Pasture {
   // - 동물들이 뛰어다니는 기능
   fun run(animalList: ArrayList<Animal>){
       for(animal in animalList){
           if(animal is RunAnimal2){
               animal.run()
           }
       }
   }
}

// 밀림
class Jungle {
   // - 동물들이 사냥하는 기능
   fun hunt(animalList: ArrayList<Animal>){
       for(animal in animalList){
           if(animal is HuntAnimal){
               animal.hunt()
           }
       }
   }
}

// 돌고래
// - 타입, 크기, 헤엄속도
//class  Dolphin(var type:String, var size:String, var swimSpeed:Int) {
//
//    // - 밥을 먹는 기능
//    fun eat(){
//
//    }
//    // - 헤엄치는 기능
//    fun swimming(){
//
//    }
//    // - 정보를 출력하는 기능
//    fun printInfo(){
//
//    }
//}

class Dolphin(type: String, size: String, swimSpeed: Int) : MarinAnimal(type, size, swimSpeed) {
   override fun swimming() {
       println("${type}은 돌~돌~ 하면서 헤엄칩니다")
   }

   override fun eat() {
       println("${type}은 냠냠냠 하고 먹습니다")
   }

   override fun printInfo() {
       println("타입 : ${type}")
       println("크기 : ${size}")
       println("헤엄속도 : ${swimSpeed}노트")
   }
}

// 상어
// - 타입, 크기, 헤엄속도
//class Shark (var type:String, var size:String, var swimSpeed:Int) {
//    // - 밥을 먹는 기능
//    fun eat(){
//    }
//    // - 헤엄치는 기능
//    fun swimming(){
//    }
//    // - 사냥하는 기능
//    fun hunt(){
//    }
//    // - 정보를 출력하는 기능
//    fun printInfo(){
//    }
//}

class Shark(type: String, size: String, swimSpeed: Int) : MarinAnimal(type, size, swimSpeed), HuntAnimal{
   override fun swimming() {
       println("${type}은 슈웅 슈융하며 헤엄 칩니다.")
   }

   override fun eat() {
       println("${type}은 얌얌얌하며 먹습니다")
   }

   override fun printInfo() {
       println("타입 : $type")
       println("크기 : $size")
       println("헤엄 속도 : ${swimSpeed}노트")
   }

   override fun hunt() {
       println("${type}은 으아아아아아!!! 하면서 사냥합니다")
   }

}

// 말
// - 타입, 크기, 달리기 속도
//class Horse (var type:String, var size:String, var runSpeed:Int) {
//    // - 밥을 먹는 기능
//    fun eat(){
//    }
//    // - 뛰어 다니는 기능
//    fun run(){
//    }
//    // - 정보를 출력하는 기능
//    fun printInfo(){
//    }
//}

class Horse(type: String, size: String, runSpeed: Int) : RunAnimal1(type, size, runSpeed), RunAnimal2{
   override fun eat() {
       println("${type}은 당근 당근하며 먹습니다")
   }

   override fun printInfo() {
       println("타입 : $type")
       println("크기 : $size")
       println("달리기 속도 : ${runSpeed}km/h")
   }

   override fun run() {
       println("${type}은 이히히히힝~하며 달립니다.")
   }

}


// 기린
// - 타입, 크기, 달리기 속도
//class Giraffe (var type:String, var size:String, var runSpeed:Int) {
//    // - 밥을 먹는 기능
//    fun eat(){
//    }
//    // - 뛰어 다니는 기능
//    fun run(){
//    }
//    // - 사냥하는 기능
//    fun hunt(){
//    }
//    // - 정보를 출력하는 기능
//    fun printInfo(){
//    }
//}
class Giraffe(type: String, size: String, runSpeed: Int) : RunAnimal1(type, size, runSpeed), RunAnimal2, HuntAnimal{
   override fun eat() {
       println("${type}은 풀풀풀하며 먹습니다")
   }

   override fun printInfo() {
       println("타입 : $type")
       println("크기 : $size")
       println("달리기 속도 : ${runSpeed}km/h")
   }

   override fun run() {
       println("${type}은 영차~ 영차~하며 뜁니다")
   }

   override fun hunt() {
       println("${type}은 가즈아~하며 사냥합니다")
   }

}

// 호랑이
// - 타입, 크기, 먹이량
//class Tiger (var type:String, var size:String, var feeVolume:Int){
//    // - 밥을 먹는 기능
//    fun eat(){
//    }
//    // - 뛰어 다니는 기능
//    fun run(){
//    }
//    // - 정보를 출력하는 기능
//    fun printInfo(){
//    }
//}
class Tiger(type: String, size: String, feeVolume: Int) : EatAnimal(type, size, feeVolume), RunAnimal2{
   override fun eat() {
       println("${type}은 아작 아작하며 먹습니다")
   }

   override fun printInfo() {
       println("타입 : $type")
       println("크기 : $size")
       println("먹이량 : ${feeVolume}km/h")
   }

   override fun run() {
       println("${type}은 헥~ 헥~하며 뜁니다")
   }

}


// 사자
// - 타입, 크기, 먹이량
//class Lion  (var type:String, var size:String, var feeVolume:Int){
//    // - 밥을 먹는 기능
//    fun eat(){
//    }
//    // - 사냥하는 기능
//    fun hunt(){
//    }
//    // - 정보를 출력하는 기능
//    fun printInfo(){
//    }
//}
class Lion(type: String, size: String, feeVolume: Int) : EatAnimal(type, size, feeVolume), HuntAnimal{
   override fun eat() {
       println("${type}은 꿀꺽 꿀꺽하며 먹습니다")
   }

   override fun printInfo() {
       println("타입 : $type")
       println("크기 : $size")
       println("먹이량 : ${feeVolume}km/h")
   }

   override fun hunt() {
       println("${type}은 암컷아 사냥해와라~하며 사냥합니다")
   }

}

// 모든 동물들
//open class Animal(var type:String, var size:String){
//    open fun eat(){
//
//    }
//    open fun printInfo(){
//
//    }
//}

open abstract class Animal(var type:String, var size:String){
   open abstract fun eat()

   open abstract fun printInfo()
}

// 해양 생물
//open class MarinAnimal(var swimSpeed: Int){
//    open fun swimming(){
//
//    }
//}

// 해양 생물
open abstract class MarinAnimal : Animal{

   var swimSpeed = 0
   // 매개 변수로 받은 값들 중 type과 size는 부모클래스의 생성자를 호출 할 때 전달 해준다.
   constructor(type:String, size:String, swimSpeed: Int) : super(type, size){
       this.swimSpeed = swimSpeed
   }

   open abstract fun swimming()
}

// 달리는 동물2
//open class RunAnimal2(){
//    open fun run(){
//
//    }
//}

interface RunAnimal2{
   fun run()
}

// 달리는 동물1
// open class RunAnimal1 (var runSpeed: Int)
open abstract class RunAnimal1 : Animal{

   var runSpeed = 0

   constructor(type:String, size:String, runSpeed: Int) : super(type, size){
       this.runSpeed = runSpeed
   }
}

// 먹는 동물
// open class EatAnimal(var feeVolume: Int)
open abstract class EatAnimal : Animal{

   var feeVolume = 0

   constructor(type:String, size:String, feeVolume: Int) : super(type, size){
       this.feeVolume = feeVolume
   }
}



// 사냥하는 동물
//open class HuntAnimal(){
//    open fun hunt(){
//
//    }
//}

interface HuntAnimal{
   fun hunt()
}
profile
성장하는 개발자

0개의 댓글