Android 앱개발 공부/TIL(Today I Learned)

[Android] TIL 11일차

bunny code 2024. 6. 4. 20:36

메소드 설계


 

* 메소드 설계

- 특정한 로직을 가지는 소스코드에 별명(이름)을 붙일 수 있음

- 이름이 붙은 로직을 메소드라고 칭함

 

메소드 사용 이유

- 로직을 추상화해 놓고 상황에 맞게 실행할 수 있음

- 코드의 재사용성을 높임

 

 

* 메소드 구조

fun 메소드이름(변수명:자료형, 변수명:자료형 ....) : 반환자료형 {
		소스코드 로직
}

-> fun : function의 약자

 

 

* 메소드 예제

fun main() {
    var num1 = readLine()!!.toInt()
    var num2 = readLine()!!.toInt()

    // sum이라는 이름의 메소드를 호출!
    sum(num1, num2)
}

fun sum(num1:Int, num2:Int) {
    var result = num1 + num2
    println("num1과 num2의 덧셈결과는 ${result}입니다.")
}

-> sum(메소드 이름)은 다른 명으로 바꿔도 됨

-> 반환자료형이 없을 때생략하거나 : Unit을 작성하면 됨

 

readLine() : 사용자로부터 문자열(String) 입력을 받아 반환하는 함수,!!(널 단언 연산자)가 있어서 절대 Null값이 들어올 수 없음

toInt() : 입력받은 데이터를 자료형 Int로 변환

+ kotlin에서 항상 프로그램의 시작점은 main 함수

 

* 메소드 예제 (2)

fun main() {
    var myMathScore = readLine()!!.toInt()
    
    var myRank = checkRank(myMathScore)
    println("나의 등급은 : ${myRank}")
}

fun checkRank(score:Int) : String {
		return when(score) {
			in 90..100 ->	return "A"
			in 80..89 -> return "B"
			in 70..79 -> return "C"
			else -> return "D"
		}
}
fun main(){
    var score = readLine()!!.toInt()

    var myRank = test(score)
    println("내 등급은 ${myRank}")
}

fun test(num:Int):String{
    if(num>=90){
        return "A"
    } else if(num>=80){
        return "B"
    } else if(num>=70){
        return "C"
    }
    else
        return "D"
}

-> 밑에는 kotiln 컨벤션을 적용하지 않은 형태(else if가 많으면 when을 쓰는 것을 미적용)

 

 

* 예제 변형

package com.example.myapplication

fun main() {
   var myMathScore = readLine()!!.toInt()

    var myRank = checkRank(myMathScore)
    println("나의 등급은 : ${myRank}")
}

fun checkRank(score:Int) : Char {
    return when(score) {
        in 90..100 -> return 'A'
        in 80..89 -> return 'B'
        in 70..79-> return 'C'
        else -> return 'D'
    }
}

-> return값이 영어 한 글자라 문자열(String) 대신 하나의 문자(Char)를 반환자료형으로 써도 되는 걸 확인했다. (단, 작은따옴표로 변경해야 함)

 

 

 

클래스 설계


* 클래스 설계

클래스 설계를 알려면 먼저 객체지향 프로그래밍에 대해 알아야 한다.

 

객체지향 프로그래밍(Object Oriented Programming, OOP) 원칙을 지키면 이런 특징을 가진다.

  • 코틀린은 모든 것이 클래스형태이므로 객체화할 수 있음
  • 프로그램에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만듦
  • 객체들 간의 적절한 결합을 통해 유지보수를 쉽게 함

객체지향 5대 키워드(클래스, 추상화, 캡슐화, 상속, 다형성)

 

 

* 클래스

  • 프로그램의 각 요소별 설계도로 이해
  • kotlin에선 class 키워드를 활용하여 클래스 생성
  • 클래스에는 정보(프로퍼티)와 행위(메소드)를 작성
  • 보통 하나의 파일 하나의 클래스지만 여러 개의 클래스도 존재할 있음

 

* 클래스 구조

class 클래스이름 {
	  정보1
		정보2

		행위1
		행위2
}

 

 

* 데이터 클래스

- 정보(프로퍼티)만 가지고 있는 클래스 필요시 생성

- 매우 유용한 메소드를 자동 생성해 줌(밑에 작성)

  • hashCode(): 객체를 구분하기 위한 고윳값을 리턴
  • eauals(): 동일한 객체인지 비교해서 true 또는 false를 리턴
  • copy(): 현재 객체의 모든 정보를 복사해서 새로운 객체를 리턴
  • toString(): 현재 객체의 모든 정보(프로퍼티)를 예쁘게 출력
  • getXXX()/setXXX(매개변수): 변수의 값을 리턴하거나 설정

 

* 데이터 클래스 구조

data class 클래스이름 {
			정보1
			정보2
}

 

 

 

* 실드 클래스

  • 상속과 관련된 개념
  • 상속받을 있는 자식클래스들을 미리 정의, 무분별한 상속 방지

* 실드 클래스 구조

sealed class 부모클래스 {
	class 자식클래스1 : 부모클래스생성자
	class 자식클래스2 : 부모클래스생성자
}

 

* 오브젝트 클래스 -> 고급 문법 시간에 다시 설명

  • Java static 대신 사용하는 키워드

* 열거 클래스 -> 심화까지 배우고 다시 확인할 것

 

 

 

생성자의 활용


* 생성자의 활용

  • 기본 생성자와 명시적 생성자가 존재(기본 생성자는 기존에 클래스를 만드는 행위와 다를 게 없음)
  • 명시적 생성자는 주 생성자와 부 생성자로 구분 

 ex) 항상 <이름, 머리색상, 키>만 생성자에서 최초로 코드를 실행할 때는 주 생성자를 사용,

그렇지 않고 <이름, 머리색상> 또는 <이름, 머리색상, 키> 등 여러 개의 생성자를 통해 최초로 코드를 실행할 때는 생성자를 사용

 

 

* 주(init) 생성자 사용 예시

fun main() {

}

// 클래스 선언부에 생성자를 명시함
class Character(_name:String, _hairColor:String, _height:Double) {
    var name:String = ""
    var hairColor:String = ""
    var height:Double = 0.0

		// 매개변수를 직접 넘기지않음
    init {
        println("매개변수없는 생성자 실행 완료!")
    }

    fun fireBall() {
        println("파이어볼!")
    }
    fun compositing(device1:String, device2:String): String {
        var device3 = device1 + device2
        println("새로운 무기인 ${device3}입니다")
        return device3
    }
}

 

 

* 부 생성자(constructor) 사용 예시

	fun main() {

  }

  class Character {
      var name:String = ""
      var hairColor:String = ""
      var height:Double = 0.0

      // 명시적 생성자 (Constructor)
      // _name, _hairColor, _height와 같이 생성자에 변수를 넘기는 경우에 사용함
      constructor(_name:String, _hairColor:String, _height:Double) {
          println("${_name}을 생성자로 넘겼어요")
          println("${_hairColor}를 생성자로 넘겼어요")
          println("${_height}를 생성자로 넘겼어요")
      }

      fun fireBall() {
          println("파이어볼!")
      }
      fun compositing(device1:String, device2:String): String {
          var device3 = device1 + device2
          println("새로운 무기인 ${device3}입니다")
          return device3
      }
  }

 

* 주 생성자와 부 생성자의 차이 : 기본적으로 주 생성자는 init, 부 생성자는 constructor을 사용하고 주 생성자는 클래스 정의부 옆에 프로퍼티를 작성, 부 생성자는 클래스 내부에 프로퍼티를 작성하는 차이점이 있다.

 

 

 

 

객체의 활용


 

* 객체의 활용

 

객체란? 

  • 모든 인스턴스를 포함하는 개념
  • 클래스 타입으로 선언된 것을 객체라고 함

인스턴스란?

  • 클래스로 설계된 객체를 실체화하는 것
  • 메모리 공간을 차지함

클래스 실체화

  • 정보와 행위를 작성한 클래스를 실체화해서 프로그램에 로딩 (메모리에 적재)
  • 정보가 행위가 그대로 로딩되는 것이 아니라 위치정보를 메모리에 로딩
  • 프로그램은 객체의 위치정보를 변수에 저장해두고, 필요할 참조

 

* 객체 예제 코드

fun main() {
		// 불마법사로 객체화
    var magicianOne = Character("불마법사", "red", 180.2)
		println("${magicianOne.name}의 머리색상은 ${magicianOne.hairColor}입니다")
		magicianOne.fireBall()

		// 냉마법사로 객체화
    var magicianTwo = Character("냉마법사", "blue", 162.2, 25, "여")
		println("${magicianTwo.name}의 머리색상은 ${magicianTwo.hairColor}이고 나이는 ${magicianTwo.age}입니다.")
		magicianTwo.fireBall()
}

class Character {
    var name:String = ""
    var hairColor:String = ""
    var height:Double = 0.0
    var age:Int = 0
    var gender:String = ""

    // 명시적 생성자 (Constructor)
    // _name, _hairColor, _height와 같이 생성자에 변수를 넘기는 경우에 사용함
    constructor(_name:String, _hairColor:String, _height:Double) {
        println("${_name}을 생성자로 넘겼어요")
        println("${_hairColor}를 생성자로 넘겼어요")
        println("${_height}를 생성자로 넘겼어요")
				name = _name
				hairColor = _hairColor
				height = _height
    }
    // _name, _hairColor, _height, _age, _gender와 같이 생성자에 변수를 넘기는 경우에 사용함
    constructor(_name:String, _hairColor:String, _height:Double, _age:Int, _gender:String) {
        println("${_name}을 생성자로 넘겼어요")
        println("${_hairColor}를 생성자로 넘겼어요")
        println("${_height}를 생성자로 넘겼어요")
        println("${_age}를 생성자로 넘겼어요")
        println("${_gender}를 생성자로 넘겼어요")

				name = _name
				hairColor = _hairColor
				height = _height
				age = _age
				gender = _gender
    }

    fun fireBall() {
        println("파이어볼!")
    }
}

 

-> 인스턴스 된 클래스 Character의 name, hairColor, height 속성을 가져와서 magicainOne 변수에 입력(객체화)

-> 그리고 println문에 이름(magicianOne.name)과 머리 색깔(magician.Color)이 나오도록 입력

-> 메소드 fireBall()에 있는 println문이 출력되도록 magicianOne.fireBall() 입력

-> magicianTwo는 클래스 Character에 있는 2번째 부 생성자를 이용한 것 말고는 magicianOne과 내용이 거의 흡사함

 

결괏값

-> 부생성자로 넘긴 내용들이 먼저 나오고 그 이후 작성한 println문과 fireBall() 내용이 출력되는 걸 볼 수 있음

 

 

 

상속


 

* 상속

  • 상속을 통해 클래스 간의 관계를 더 끈끈하게 하기 위함
  • 코틀린은 다른 언어들과 달리 생략된 final 키워드로 기본적으로 상속을 막아둠 -> 무분별한 상속으로 예상치 못한 흐름을 방지하기 위함
  • 코틀린은 open 키워드를 활용해서 상속 관계를 만들 수 있음

상속이 필요한 이유 : 클래스의 내용을 변경해야하는경우 부모 클래스만 변경하는 것으로 공수를 줄일 있음

 

* 상속 예제

fun main() {
    var bird = Bird("새")
    var chicken = Chicken("닭")
    var sparrow = Sparrow("참새")
    var pigeon = Pigeon("비둘기")

    bird.fly()
    chicken.fly()
    sparrow.fly()
    pigeon.fly()
}

open class Bird(name:String) {
    var name: String = ""

    init {
        // this는 현재 클래스의 상태변수를 의미합니다
        // var name: String = ""
        this.name = name
    }

    fun fly() {
        println("${name} 날아요~")
    }

}

class Chicken(name: String) : Bird(name) {

}

class Sparrow(name: String) : Bird(name) {

}

class Pigeon(name: String) : Bird(name) {

}

-> fly 메소드가 Bird에만 있음에도 불구하고 chicken, sparrow, pigeon 모두 fly를 쓸 수 있음

-> 셋 다 Bird를 상속받았기 때문에 이처럼 사용이 가능함(코드가 효율적임)

-> 부모 클래스에 생성자가 있을 경우 자식 클래스들도 파라미터를 작성해야 부모 클래스로 넘겨진다

 

* 자식 클래스의 경우 name : String 앞에 var 또는 val이 없으면 프로퍼티가 아닌 파라미터

 

 

 

오버라이딩


 

* 오버라이딩

  • 부모 클래스의 정보(프로퍼티)나 행위(메소드)를 재설계 -> 보통 행위를 재설계, 이 것을 오버라이딩이라 함
  • 오버라이딩 단축키 command + O

* 오버라이딩 예제

fun main() {
    var bird = Bird("새")
    var chicken = Chicken("닭", 2)
    var sparrow = Sparrow("참새", "갈색")
    var pigeon = Pigeon("비둘기", "서울")

    bird.fly()
    chicken.fly()
    sparrow.fly()
    pigeon.fly()
}

open class Bird(name:String) {
    var name: String = ""

    init {
        this.name = name
    }

    open fun fly() {
        println("${name}은 날아요~")
    }

}

class Chicken(name: String, age: Int) : Bird(name) {
    var age:Int = 0

    init {
        this.age = age
    }

    override fun fly() {
        println("${age}살의 ${name}가 날아봅니다~ 꼬끼오!")
    }
}

class Sparrow(name: String, color: String) : Bird(name) {
    var color:String = ""

    init {
        this.color = color
    }

    override fun fly() {
        println("${color}의 ${name}이 날아봅니다~ 짹짹!")
    }
}

class Pigeon(name: String, address: String) : Bird(name) {
    var address: String = ""

    init {
        this.address = address
    }

    override fun fly() {
        println("${address} 살고있는 ${name}가 날아봅니다~ 구구!")
    }
}

-> override fun fly(){ }를 생성할 시 기본적으로 super.fly()가 생성되는데 이것은 부모객체의 fly 메소드를 부르는 걸 의미. (해당 코드에서는 필요 없어서 생략)

 

 

 

오버로딩


 

* 오버로딩

  • 동일한 이름의 메소드를 여러 형태로 만들 수 있음. -> 매개 변수의 개수나 자료형을 다르게 해야 함
  • 반환자료형(반환형) 오버로딩에 영향을 주지 않음 -> 다른 메소드와 반환자료형이 같거나 달라도

* 오버로딩 예제

package com.example.myapplication

fun main() {
    var calc = Calculator()

    var intResult = calc.add(1,2)
    var doubleResult = calc.add(1.2, 2.2)

    println("정수 덧셈결과: ${intResult}")
    println("실수 덧셈결과: ${doubleResult}")

}

class Calculator {

    fun add(num1: Int, num2: Int): Int {
        return num1+num2
    }

    fun add(num1: Double, num2: Double): Double {
        return num1+num2
    }
}

-> add 메소드를 두 개 썼지만 Int, Double 두 가지의 자료형을 썼기에 오버로딩이 가능함

 

package com.example.myapplication

fun main() {
    var calc = Calculator()

    var intResult = calc.add(1,2)
    var doubleResult = calc.add(1, 3,5,6)

    println("정수 덧셈결과: ${intResult}")
    println("정수 덧셈결과: ${doubleResult}")

}

class Calculator {

    fun add(num1: Int, num2: Int): Int {
        return num1+num2
    }

    fun add(num1: Int, num2: Int, num3: Int, num4: Int): Int {
        return num1+num2+num3+num4
    }
}

-> 위 코드처럼 변수 자료형이 아닌 개수를 다르게 했을 때도 정상적으로 오버로딩 되는 걸 확인할 수 있음

 

 

 

참고 자료 및 보면 유용한 자료


https://taetoungs-branch.tistory.com/73

 

[Kotlin] ?(물음표)와 !!(느낌표 두개)의 사용

코틀린에서 물음표(?)와 느낌표 두개(!!)의 경우 null의 사용과 관련되어있는 문자임 코틀린은 다른 언어들보다도 null에 민감해서 변수를 선언할 때 null의 여부에 대해 표시해주어야 하는 경우가

taetoungs-branch.tistory.com

해당 글을 통해!!(널 단언 연산자)의 의미를 알게 되었다.

 

https://juyeop.tistory.com/50

 

Kotlin 코틀린의 클래스와 객체

안녕하세요, 오늘은 Kotlin 코틀린 언어에서의 클래스와 객체에 대해서 알아보려고 합니다. 객체 지향 프로그래밍의 기본 개념과 여러 가지의 중요한 특징들에 대해서 살펴보도록 하겠습니다.

juyeop.tistory.com

프로퍼티나 메소드와 같이 코틀린에서 사용하는 언어들의 개념들을 간단하게 정리해 준 글

 

 

* 파라미터와 프로퍼티의 차이 : 명칭도 비슷하고 구조도 비슷해서 헷갈렸다.. 명확하게 구분하기 위해 챗 GPT에게 질문함

 

파라미터 : "변수명 : 해당 변수 자료형"으로 구성되어 있고, 메서드 바로 옆 정의부에 선언

프로퍼티 : 파라미터와 구성은 유사하지만, 클래스 내부에 선언됨(단, 주 생성자와 같이 클래스 정의부 옆에 작성되는 것도 프로퍼티 취급)

 

 

 

배우면서 느낀 점


인스턴스, 객체, 파라미터, 프로퍼티 등의 단어들이 무슨 의미인지 어떻게 쓰는 건지 무슨 차이가 있는지 하나하나 짚고 넘어가다 보니 생각보다 오래 걸렸다. 문법 기초를 배우는 기간에 이런 것들을 찾아보고 제대로 이해해 둬야 나중에 가서 고생하지 않을 거라는 생각이 들어서 찾아봤는데 하길 잘했다는 생각이 들었다.

'Android 앱개발 공부 > TIL(Today I Learned)' 카테고리의 다른 글

[Android] TIL 13일차  (0) 2024.06.07
[Android] TIL 12일차  (0) 2024.06.05
[Android] TIL 10일차  (0) 2024.06.03
[Android] TIL 9일차  (0) 2024.05.31
[Android] TIL 8일차  (0) 2024.05.30