컴굥일지

[Kotlin] 코틀린 객체 지향 프로그래밍 기초 본문

프로그래밍 강의/Kotlin

[Kotlin] 코틀린 객체 지향 프로그래밍 기초

gyong 2022. 12. 28. 23:04
반응형

코틀린 객체 지향 프로그래밍 기초

OOP 소개

  • OOP : Object Oriented Programming
  • ex) Java, C#, Kotlin...
  • 5가지 기본 개념
    • 변수와 타입
      • 변수 : 저장 공간에서의 위치 (저장 공간을 나타내기 위해 고유의 이름이 필요 -> 이름으로 데이터에 접근)
    • 흐름 제어
    • 함수
      • 코드를 분리해준다.
      • 코드를 필요할 때 불러 쓸 수 있다.
        => 재사용이 쉽다
    • colletions
      • 많은 요소를 한 군데에 저장해준다.
      • 반복을 쉽게 사용할 수 있게 해준다.
    • (상속을 포함한) 클래스와 객체
      • 직접 데이터 타입을 만들게 해준다.
      • 데이터 멤버와 메소드를 한 곳에 있게 해준다.
      • 가독성있고 유지 가능한 코드를 쓸 수 있게 해준다.

클래스와 객체

  • 클래스 != 객체
  • 클래스 : 속성와 기술을 정의해주는 도안
  • 객체 : 클래스로 만든 무언가

클래스와 초기화

fun main() {
    var Jini = Person("Jingyeong","Lee")
    var john =Person()
    var peter=Person(lastName = "Peter")
}
class Person2 // 이 자체로도 유효함
// constructor는 객체 생성시 값을 추가하게 해준다 (선택임)
// private, public 등을 추가할 수도 있음
class Person(firstName:String = "John", lastName:String="Doe"){ // default 값을 줄 수도 있다
    init { // Initializer : 객체가 생성되는 순간 호출되는 것
        println("$firstName created")
    }
}

멤버 변수 - 기능과 생성자

  • 코틀린의 클래스는 하나의 primary constructor와 다수의 secondary constructor를 가질 수 있습니다.
  • 그리고 primary constructor의 경우 클래스 헤더(클래스 이름 오른편)에 위치합니다.
  • primary constructor에서는 어떤 실행문도 포함될 수 없습니다.
    만약 초기화하는 코드를 넣고 싶을 경우 init 블럭을 이용하면 가능합니다.
  • 그리고 primary constructor는 secondary constructor의 첫 번째 실행문으로 실행됩니다.
  • 따라서 init 블럭의 코드는 항상 secondary constructor의 body보다 먼저 실행됩니다.
// constructor는 객체 생성시 값을 추가하게 해준다 (선택임)
// private, public 등을 추가할 수도 있음
class Person(firstName: String = "John", lastName: String = "Doe") { // default 값을 줄 수도 있다
    // Member Variables - Properties
    var age: Int? = null
    var hobby: String = "watch Netflix"
    init { // Initializer : 객체가 생성되는 순간 호출되는 것
        println("$firstName created")
        // println("My age is $age") // constructor로 입력 받은 것은 출력되지 않는다
    }
    // Member secondary Constructor
    constructor(firstName: String, lastName: String, age: Int) : this(firstName, lastName) {
        this.age=age
    }
    // Member functions - Methods
    fun stateHobby() {
        println("My hobby is $hobby")
        // println("$firstName")  <-- 여기서는 안 보임. init과 constructor에서만 보임
        // 멤버 변수에 선언을 하고 추가하는 과정이 필요함
    }
}

늦은 초기화와 setter, getter

fun main() {
    var myCar=Car()
    println("myCar brand: ${myCar.myBrand}")
    myCar.maxSpeed=240 // set value
    println("Max Speen id ${myCar.maxSpeed}") // get value
    // myCar.myModel=~~  <-- private이라 에러
    println(myCar.myModel)
}
class Car(){
    // lateinit 나중에 초기화 하겠다는 표시
    // 변수를 초기화하기 전에 사용하지 않는 것을 주의하자
    lateinit var owner:String
    val myBrand:String ="BMW"
        // custom getter
    get(){
        return field.lowercase()
    }
    var maxSpeed:Int =250
        //아래 get,set은 코틀린이 자동으로 생성 - 우리가 직접 쓸 필요 없음
//    get()=field
//    set(value){
//        field=value
//    }
    // field는 getter, setter 메서드 안에서 프로퍼티 참조를 도와주며 뒷받침 하는 필드이다. 이는 필수이다.
    // 만약 getter, setter 안에서 프로퍼티를 입력하면, 반복되는 호출이 일어나서 스택 오버플로우 현상이 일어나게 된다.
    // value는 변수에 정해지는 값이다.
        //custom setter
    set(value){
        field = if(value>0) value else throw java.lang.IllegalArgumentException("Max Speen can not be less than 0")
    }
    var myModel: String ="M5"
    private set  // 같은 클래스 내에서만 쓸 수 있음
    init{
        this.myModel="M3" //private 이어도 이건 가능
        this.owner="Frank"
    }
}

데이터 클래스

//매개변수 1개는 반드시 필요 (var이나 val로 넣어야함)
// abstract, open, sealed, inner 클래스일 수 없다
data class User(val id:Long, var name:String)
fun main() {
    val user1=User(1,"Denis")
    user1.name="Michael"
    val user2=User(1,"Michael")
    println(user1.equals(user2))
    println(user1==user2)
    println("User Details: $user1")
    val updatedUser=user1.copy(name = "Denis Panjuta") // 카피할 수 있음. (하면서 수정 가능)
    println(user1)
    println(updatedUser)
    val updatedUser2=user1 // 이렇게 바꾸면 동일해짐
    updatedUser2.name="Jini"
    println(user1)
    println(updatedUser2)
    println(updatedUser.component1()) // 1
    println(updatedUser.component2()) // Denis Panjuta
    val (id, name) =updatedUser
    println("id=$id, name=$name")
}

상속 (Inheritance)

// 코틀린에서 클래스느 기본적으로 default라서, 따로 open을 넣어줘야 상속이 가능하다
// sealed를 넣으면 상속할 수 없다
// 모든 클래스는 Any라는 클래스를 상속받는다
// Super/Parent/Base Class
open class Car(val name:String, val brand:String){ // open이어야지 상속이 될 수 있다.(부모)
    open var range:Double = 0.0 // 프로퍼티가 open이어야 자식에서 수정 가능
    fun extendRange(amount:Double){
        if(amount>0) range+=amount
    }
    open fun drive(distance:Double){
        println("Drove for $distance KM")
    }
}
// Sub/Child/Derived class of Car
// 상속받는 클래스의 특성은 다 가져야 하지만, 추가할 수는 있다
class ElectricCar(name:String, brand: String, batteryLife:Double): Car(name,brand){
    override var range=batteryLife*6
    override fun drive(distance: Double) {
        println("Drove for $distance Km on electricity")
    }
    fun drive(){
        println("Drove for $range Km on electricity")
    }
}
fun main(){
    var myCar=Car("A3", "Audi")
    var myECar=ElectricCar("S-Model", "Tesla", 85.0)
    myCar.drive(200.0)
    // Polymorphism(다형성) : 비슷한 특성을 가진 객체들이 공통된 방법으로 여겨지는 것
    myECar.drive(200.0)
    myECar.drive() // 선택 가능
}

인터페이스

  • 인터페이스는 클래스 기능을 확장하게 해준다
  • 인터페이스는 기본적으로 클래스가 서명할 수도 있는 계약서이다.
    서명하면, 클래스는 인터페이스의 프로퍼티와 함수 구현을 제공할 의무를 가진다.
  • 왜 사용할까?
    • 나중에 구현하고 싶은 특정 함수와 클래스 프로퍼티가 있다면 유용하다. (지금 당장 구현하고 싶지 않은 경우)
    • 구체적인 함수 body를 아직 만들고 싶지 않을 때
// 인터페이스는 모든 프로퍼티와 함수를 구현해줄 수 있지만, 꼭 그래야 하는 것은 아니다
// 그리고 구현된 부분은, 이 인터페이스를 사용하는 클래스가 오버라이딩 할 수도 있다
interface Drivable{
    val maxSpeed: Double
    fun drive():String // 함수 body 없이 header만 존재 - 클래스에서 구현해줘야 함
    fun brake(){
        println("The drivable is braking")
    }
}
// Super/Parent/Base Class
// override 하려면 키워드 추가해야 함
open class Car(override val maxSpeed:Double, val name:String, val brand:String): Drivable{
    open var range:Double = 0.0 // 프로퍼티가 open이어야 자식에서 수정 가능
    fun extendRange(amount:Double){
        if(amount>0) range+=amount
    }
    // override fun drive(): String = "driving the interface drive"  <-- 이렇게 적어도 동일함함
   override fun drive(): String {
        return "driving the interface drive"
    }
    open fun drive(distance:Double){ //return이 string이 아니라서 위에 것을 만들어야 함
        println("Drove for $distance KM")
    }
}
// Sub/Child/Derived class of Car
// 상속받는 클래스의 특성은 다 가져야 하지만, 추가할 수는 있다
class ElectricCar(maxSpeed: Double, name:String, brand: String, batteryLife:Double): Car(maxSpeed,name,brand){
    override var range=batteryLife*6
    override fun drive(distance: Double) {
        println("Drove for $distance Km on electricity")
    }
    override fun drive():String{ //<-- 인터페이스로부터 온 것이 되니까 오버라이드 해야함
       return "Drove for $range Km on electricity"
    }
    override fun brake() {
        super.brake()
        println("This is sub")
    }
}

추상 클래스

  • abstract 클래스는 객체를 만들 수 없다.
  • sub 클래스에서 상속은 받을 수 있다.
  • 추상 클래스에서 프로퍼티와 메서드는 abstract 키워드를 따로 사용하지 않는 한 추상적이지 않다.

    인터페이스와 추상 클래스의 차이

  • 인터페이스는 state를 저장할 수 없고 여러 인터페이스를 구현할 수 있지만, 클래스는 한개만 상속받을 수 있다
  • 추상클래스는 생성자가 있지만, 인터페이스는 없다.
  • 인터페이스는 필드를 저장하지 못한다.
  • 추상 클래스는 인터페이스가 추가할 수 있는건 모두 할 수 있다. (필드와 생성자 추가)
    => 그래서 state가 잘 저장된다
    // An abstract class cannot be instantiated
    // (you cannot create objects of an abstract class).
    // However, you can inherit subclasses from can them.
    // The members (properties and methods) of an abstract class are non-abstract
    // unless you explicitly use the abstract keyword to make them abstract.
    abstract class Mammal(val name: String, val origin: String, val weight: Double) {   // Concrete (Non Abstract) Properties
    // Abstract Property - 자식 클래스에서 반드시 override 해야 함
    abstract var maxSpeed: Double
    // Abstract Methods (Must be implemented by Subclasses)
    abstract fun run()
    abstract fun breath()
    // Concrete (Non Abstract) Method
    fun displayDetails() {
        println("Name: $name, Origin: $origin, Weight: $weight, " +
                "Max Speed: $maxSpeed")
    }
    }
    class Human(name: String, origin: String, weight: Double, override var maxSpeed: Double): Mammal(name, origin, weight) {
    override fun run() {
        // Code to run
        println("Runs on two legs")
    }
    override fun breath() {
        // Code to breath
        println("Breath through mouth or nose")
    }
    }
    class Elephant(name: String, origin: String, weight: Double, override var maxSpeed: Double): Mammal(name, origin, weight) {
    override fun run() {
        // Code to run
        println("Runs on four legs")
    }
    override fun breath() {
        // Code to breath
        println("Breath through the trunk")
    }
    }
    fun main() {
    val human = Human("Denis", "Russia", 70.0, 28.0)
    val elephant = Elephant("Rosy", "India", 5400.0, 25.0)
    //val mammal = Mammal("Denis","Russia",70.0, 28.0) <-- 추상 클래스는 객체 생성 불가능
    human.run()
    elephant.run()
    human.breath()
    elephant.breath()
    }

형변환

fun main() {
    val obj1:Any ="I have a dream"
    if(obj1 !is String){
        println("not a string")
    }else{
        println("string") // 자동으로 스트링으로 캐스트 됨
        println(obj1.length)
    }
    // as 키워드를 사용하여 explicit unsafe 캐스팅을 함
    // string이 아니면 문제가 생긴다
    var str1:String =obj1 as String
    println(str1.length)
    var obj2:Any =1337
    //val str2:String = obj2 as String // string이 아닌데 string으로 형 변환 하려 해서 에러
    //println(str2)
    // as? 키워드를 사용하여 explicit safe 캐스팅을 함함
    var obj3:Any = 1337
    val str3 :String? =obj3 as? String
    println(str3)
}
반응형

'프로그래밍 강의 > Kotlin' 카테고리의 다른 글

[Kotlin] 코틀린 기초 더 배우기  (0) 2022.12.28
[Kotlin] 코틀린 기초  (1) 2022.12.28
Comments