[Android/Flutter 교육] 6일차

MSU·2024년 1월 3일

Android-Flutter

목록 보기
6/85
post-thumbnail

자율 프로젝트는 Flutter와 Android팀 수요를 먼저 확인후에 나눌 예정

코틀린에서는 생성자를 여러개 사용하기보다 주생성자에 default parameter를 셋팅하는걸 권장한다고 한다. Converting과 같은 경우 부생성자를 만들게 될 경우에는 정적 팩토리 메서드를 추천한다고 함

문제12

동물원에는 동물들이 여러 가지가 있다.
동물은 사자, 고양이, 새가 있다.
사자는 다리가 네개이고 어흥이라는 소리를 낸다.
고양이는 다리가 네개이고 야옹이라는 소리를 낸다.
토끼는 다리가 두개이고 짹짹이라는 소리를 낸다.

프로그램이 실행되면 3가지 동물에 관련된 정보를 입력받는다.
입력받는 정보는 동물의 종류와 다리의 개수, 내는 소리를 받는다.
입력이 완료되면 각 동물의 정보를 모두 출력하고
동물의 다리 개수 총합을 구해 출력한다.

생성자

  • 기존의 연습문제에서 생성한 클래스는 정보를 입력받는 메서드를 실행하지 않으면 해당 정보를 출력하는 메서드를 실행할 수 없는 문제가 있다. (정해진 메서드 순서를 지켜야 하는 문제)
  • 클래스를 통해 객체를 생성할 때 자동으로 수행될 코드를 작성하는 곳
  • 메서드와 비슷해 보이지만 반환 타입이 없어 "메서드라고 부르지 않는다".(return 안씀)
  • 생성자의 역할은 클래스가 가지고 있는 변수의 값을 초기화하는데 주로 이동된다.
  • 모든 클래스는 생성자를 하나 이상 갖고 있어야 한다.
  • 객체가 생성될 때 VM이 생성자를 무조건 호출한다.
  • 메서드가 정상적으로 동작하기 위해 필요한 데이터를 올바르게 셋팅하기 위해 메서드가 아닌 생성자를 셋팅함으로써 안정성과 무결성을 보장받는다.(메서드는 사용자가 생략할 수 있지만, 생성자는 무조건 실행되기 때문)
  • 무조건 동작시키기 위한 코드를 생성자에 작성(멤버 변수에 정상적인 값 저장 등)
  • 생성자는 매개변수의 형태를 다르게 하여 여러개를 만들 수 있지만 자동으로 호출되는 생성자는 무조건 한 개이다.

init 코드 블록

  • kotlin은 클래스에 init코드 블록을 만들어 주면 객체 생성 시 자동으로 처리되는 코드를 만들 수 있다.
  • 객체를 생성할 때 자동으로 동작하는 부분이다.
  • Java 코드로 변환될 때 모든 생성자의 가장 윗부분에 코드가 삽입된다.
  • 생성자에 만든 코드가 동작하기 전에 동작한다.
  • 생성자를 여러개 만들어 제공할 경우 중복되는 코드가 있을 경우 init 코드 블럭에 작성해준다.
class TestClass1{
    // init 코드 블럭
    init{
        println("TestClass1의 init 코드 블럭")
        println("객체가 생성될 때 자동으로 동작하는 부분입니다")
        println("Java 코드로 변환될 때 모든 생성자의 상단에 삽입되는 코드입니다")
    }
}

fun main() {
    // 객체를 생성한다.
    val t1 = TestClass1()
    println("t1 : $t1")
}
TestClass1의 init 코드 블럭
객체가 생성될 때 자동으로 동작하는 부분입니다
Java 코드로 변환될 때 모든 생성자의 상단에 삽입되는 코드입니다
t1 : TestClass1@fcd62180

constructor

  • 매개변수의 개수나 타입을 다르게 하면 여러개의 생성자를 제공할 수 있다.
// 생성자
class TestClass2{
    // init 블럭
    init{
        println("TestClass2의init 코드 블럭입니다")
    }

    // 생성자
    constructor(){
        println("TestClass2의 매개 변수가 없는 생성자")
    }
    
    // 매개변수의 개수나 타입을 다르게 하면 여러개의 생성자를 제공할 수 있다.
    constructor(a1:Int, a2:Int){
        println("TestClass2의 매개 변수가 있는 생성자")
        println("a1 : $a1")
        println("a2 : $a2")
    }
}

fun main() {
    // 객체 생성시 호출될 생성자를 선택해줘야 한다.
    // 클래스이름(매개변수의 형태)

    // 객체 생성시 값을 전달하지 않았기 때문에 매개변수가 없는 생성자가 호출된다.
    val t2 = TestClass2()
    println("t2 : $t2")

    println("------------------------------------------")
    // 객체 생성시 정수값 두개를 전달하였기 때문에 정수형 매개변수 두개가 있는 생성자가 호출된다.
    val t3 = TestClass2(100,200)
    println("t3 : $t3")
}
TestClass2의init 코드 블럭입니다
TestClass2의 매개 변수가 없는 생성자
t2 : TestClass2@44be181d
------------------------------------------
TestClass2의init 코드 블럭입니다
TestClass2의 매개 변수가 있는 생성자
a1 : 100
a2 : 200
t3 : TestClass2@86dcd169
  • 생성자를 작성하지 않은 클래스는 매개변수가 없고 코드가 작성되어 있지 않은 생성자가 자동으로 추가된다.
  • 생성자를 작성하게 되면 매개변수가 없고 코드가 작성되어 있지 않은 생성자가 자동으로 추가되지 않는다. (이 경우 값을 전달하지 않고 객체 생성 시 에러가 남)
  • 이를 통해 생성자를 통해서 값을 무조건 받을 수 있다.(강제성)
class TestClass3{
    var v1 = 0
    var v2 = 0

    constructor(a1:Int, a2:Int){
        println("TestClass3의 생성자 호출")
        v1 = a1
        v2 = a2
    }
}

fun main() {
    // 매개 변수가 있는 생성자 호출
    val t4 = TestClass3(100,200)
    println("t4.v1 : ${t4.v1}")
    println("t4.v2 : ${t4.v2}")
    
    // TestClass3은 매개변수가 있는 생성자를 작성해 놨기 때문에
    // 매개변수가 없는 생성자가 자동으로 추가되지 않는다.
    // val t5 = TestClass3() // 에러 남
}
TestClass3의 생성자 호출
t4.v1 : 100
t4.v2 : 200

this

  • 객체가 가지고 있는 멤버를 지칭하기 위해 사용한다.
  • 메서드나 생성자의 매개변수 이름과 객체의 멤버 변수의 이름이 같을 때 사용한다.
  • 같은 클래스 내의 다른 생성자를 호출하고자 할 때 사용한다.
class TestClass4{
    var memberA1 = 0
    var memberA2 = 0

    constructor(memberA1:Int, memberA2:Int){
        println("memberA1 : $memberA1")
        println("memberA2 : $memberA2")
    }
}

fun main() {
	val t6 = TestClass4(100,200)
    // 객체의 멤버 변수를 출력
    println("t6.memberA1 : ${t6.memberA1}")
    println("t6.memberA2 : ${t6.memberA2}")
}
memberA1 : 100
memberA2 : 200
t6.memberA1 : 0
t6.memberA2 : 0
  • 생성자나 메서드의 매개변수 이름과 멤버 변수의 이름이 같은 경우
  • 멤버 변수를 사용하고자 한다면 this를 사용한다.
  • this는 객체 자기 자신을 가르키는 키워드이다.
    constructor(memberA1:Int, memberA2:Int){
        // 여기서 사용하는 memberA1과 memberA2는 매개변수를 지칭한다.
        println("memberA1 : $memberA1")
        println("memberA2 : $memberA2")
        // 생성자나 메서드의 매개변수 이름과 멤버 변수의 이름이 같은 경우
        // 멤버 변수를 사용하고자 한다면 this를 사용한다.
        // this는 객체 자기 자신을 가르키는 키워드이다.
        this.memberA1 = memberA1
        this.memberA2 = memberA2
    }
memberA1 : 100
memberA2 : 200
t6.memberA1 : 100
t6.memberA2 : 200

생성자에서 다른 생성자를 호출

  • 자바와 같은 다른 언어에서는 생성자 블록 내에 생성자를 호출하는 코드를 제일 위에 넣었으나 코틀린에서는 콜론으로 넣어줌
  • constructor(매개변수):호출할 다른 생성자(매개변수)
  • 생성자가 호출되면 지정한 다른 생성자를 먼저 호출하여 코드가 수행되고 다른 생성자의 수행이 끝나면 생성자의 코드가 동작한다.
class TestClass4{
    var memberA1 = 0
    var memberA2 = 0

    constructor(memberA1:Int, memberA2:Int){
        // 여기서 사용하는 memberA1과 memberA2는 매개변수를 지칭한다.
        println("memberA1 : $memberA1")
        println("memberA2 : $memberA2")
        // 생성자나 메서드의 매개변수 이름과 멤버 변수의 이름이 같은 경우
        // 멤버 변수를 사용하고자 한다면 this를 사용한다.
        // this는 객체 자기 자신을 가르키는 키워드이다.
        this.memberA1 = memberA1
        this.memberA2 = memberA2
    }

    // 생성자에서 다른 생성자를 호출한다.
    // constructor(매개변수):호출할 다른 생성자(매개변수)
    // 생성자가 호출되면 지정한 다른 생성자를 먼저 호출하여 코드가 수행되고
    // 다른 생성자의 수행이 끝나면 생성자의 코드가 동작한다.
    constructor():this(1000, 2000){
        println("매개변수가 없는 생성자 호출")
    }
}


fun main() {
    val t7 = TestClass4()
    println("t7.memberA1 : ${t7.memberA1}")
    println("t7.memberA2 : ${t7.memberA2}")
}
memberA1 : 1000
memberA2 : 2000
매개변수가 없는 생성자 호출
t7.memberA1 : 1000
t7.memberA2 : 2000

주 생성자

  • 하나의 클래스에서 생성자가 여러개 있을 경우 주 생성자를 지정할 수 있다.
  • 주 생성자가 아닌 다른 생성자들은 모두 주 생성자를 무조건 호출해야 한다.
  • 주 생성자의 매개변수는 모두 멤버 변수로 정의된다.
  • constructor에 명시한 변수들은 멤버 변수로 정의가 되고 매개변수를 가지고 있는 생성자가 자동으로 추가된다. 이 생성자는 매개변수로 들어오는 값을 멤버 변수에 저장해주는 코드가 삽입된다.
  • 아래의 코드에서 TestClass6은 TestClass5와 같은 형태로 변환된다.
  • 주 생성자 정의시 constructor는 생략해도 된다.
class TestClass5{
    var a1 = 0
    var a2 = 0
    var a3 = 0

    constructor(a1:Int, a2:Int, a3:Int){
        this.a1 = a1
        this.a2 = a2
        this.a3 = a3
    }
}

// 주 생성자 사용
// TestClass5와 같은 형태로 변환된다.
class TestClass6 constructor(var a1:Int, var a2:Int, var a3:Int)
// 주 생성자 정의시 constructor는 생략해도 된다.
class TestClass6(var a1:Int, var a2:Int, var a3:Int)


fun main() {
    val t8 = TestClass5(100,200,300)
    println("t8.a1 : ${t8.a1}")
    println("t8.a2 : ${t8.a2}")
    println("t8.a3 : ${t8.a3}")
    
    val t9 = TestClass6(100,200,300)
    println("t9.a1 : ${t9.a1}")
    println("t9.a2 : ${t9.a2}")
    println("t9.a3 : ${t9.a3}")
}
t8.a1 : 100
t8.a2 : 200
t8.a3 : 300

t9.a1 : 100
t9.a2 : 200
t9.a3 : 300
  • 만약 주 생성자에 정의하지 않은 멤버 변수가 있다면 다른 생성자를 하나 만들어서 주생성자에 정의하지 않은 변수도 값을 넣어줄 수 있어야 한다.
  • 이때, 추가적으로 작성한 생성자는 반드시 주 생성자를 호출해야 한다.
  • 생성자():this() 형태로 하여 주 생성자를 반드시 호출해줘야 한다.
class TestClass7(var a1:Int, var a2:Int){
    // 주 생성자에 정의하지 않은 멤버 변수
    var a3:Int = 0

    constructor(a1:Int, a2:Int, a3:Int) : this(a1, a2) {
        this.a3 = a3
    }
}

fun main() {
    val t10 = TestClass7(100,200,300)
    println("t10.a1 : ${t10.a1}")
    println("t10.a2 : ${t10.a2}")
    println("t10.a3 : ${t10.a3}")
}
t10.a1 : 100
t10.a2 : 200
t10.a3 : 300

상속

  • 클래스를 설계할 때 다른 클래스가 가지고 있는 부분을 물려 받는 것을 의미한다.
  • 이를 통해 클래스마다 중복된 부분을 클래스 한 곳에 만들 수 있다.
  • 상속을 하는 클래스를 부모 클래스(SuperClass)라고 하며 상속을 받는 클래스를 자식 클래스(SubClass)라고 한다.
  • 코틀린에서 부모 클래스는 open키워드를 사용해야 한다. open 키워드를 사용하지 않으면 자바 코드로 변경될 때 final 키워드가 붙는다.
// 부모클래스는 open 키워드를 붙여줘야 한다.
open class SuperClass1{
    var superA1 = 100

    fun superMethod(){
        println("SuperClass1의 superMethod1")
    }
}

// class 클래스이름 : 부모클래스이름()
class SubClass1 : SuperClass1(){
    var subA2 = 200

    fun subMethod2(){
        println("SubClass1의 subMethod2")
    }
}


fun main() {
    // 자식 클래스를 통해 객체를 생성한다.
    val s1 = SubClass1()
    println(s1.superA1)
    println(s1.subA2)
    s1.superMethod()
    s1.subMethod2()
}
100
200
SuperClass1의 superMethod1
SubClass1의 subMethod2
  • 부모클래스는 자식클래스에 작성한 요소를 사용할 수 없다.
  • 부모클래스를 작성할 당시에는 자식클래스가 어떤 것들이 있는지 알수도 없고 부모클래스 작성 코드에는 자식클래스가 무엇인지 명시하는 곳이 없기 때문에 객체를 생성할 때 자식클래스 부분을 생성할 수 없다.
fun main() {
    // 부모클래스의 객체를 생성한다
    val s2 = SuperClass1()
    println(s2.SubA2) // 에러
    s2.subMethod2 // 에러
}
  • 자식클래스의 메서드에는 부모의 요소를 사용할 수 있다.
class SubClass1 : SuperClass1(){
    var subA2 = 200

    fun subMethod2(){
        println("SubClass1의 subMethod2")
        // 자식클래스의 메서드에는 부모의 요소를 사용할 수 있다.
        println("superA1 : $superA1")
        superMethod1()
    }
}

fun main() {
    val s1 = SubClass1()
    println(s1.superA1)
    println(s1.subA2)
    s1.superMethod1()
    s1.subMethod2()
}
100
200
SuperClass1의 superMethod1
SubClass1의 subMethod2
superA1 : 100
SuperClass1의 superMethod1

상속과 생성자와의 관계

  • 부모클래스의 생성자를 명시적으로 호출하지 않으면 부모클래스가 가지고 있는 생성자 중 매개변수가 없는 생성자를 자동으로 호출한다.
open class SuperClass2{
    
    constructor(){
        println("SuperClass2의 매개변수가 없는 생성자")
    }
    constructor(a1:Int){
        println("SuperClass2의 매개변수가 있는 생성자")
    }
}

// SuperClass2를 상속받은 클래스
// 자식클래스는 부모클래스의 생성자를 반드시 호출해야 한다.
class SubClass2 : SuperClass2{
    constructor(){
        // 부모클래스의 생성자를 명시적으로 호출하지 않으면
        // 부모클래스가 가지고 있는 생성자 중 매개변수가 없는 생성자를
        // 자동으로 호출한다.
    }
}

fun main(){
    val s3 = SubClass2()
    println("s3 : $s3")
}
SuperClass2의 매개변수가 없는 생성자
s3 : SubClass2@66c19234
  • 만약 부모가 가지고 있는 생성자 중 매개변수가 있는 다른 생성자를 호출하겠다면 명시적으로 호출해주면 된다.
  • super : 상속관계에서 부모를 지칭하는 키워드
open class SuperClass2{
    
    constructor(){
        println("SuperClass2의 매개변수가 없는 생성자")
    }
    constructor(a1:Int){
        println("SuperClass2의 매개변수가 있는 생성자")
    }
}

// SuperClass2를 상속받은 클래스
// 자식클래스는 부모클래스의 생성자를 반드시 호출해야 한다.
class SubClass2 : SuperClass2{
    constructor(){
        // 부모클래스의 생성자를 명시적으로 호출하지 않으면
        // 부모클래스가 가지고 있는 생성자 중 매개변수가 없는 생성자를
        // 자동으로 호출한다.
    }
    constructor(a1:Int):super(100){
        // 만약 부모가 가지고 있는 생성자 중 매개변수가 있는 다른 생성자를 호출하겠다면
        // 명시적으로 호출해주면 된다.
        // super : 상속관계에서 부모를 지칭하는 키워드
    }
}

fun main(){
    val s4 = SubClass2(100)
    println("s4 : $s4")
}
SuperClass2의 매개변수가 있는 생성자
s4 : SubClass2@cc4254
  • 아래의 코드에서 SuperClass2의 매개변수가 없는 생성자를 지우면 SubClass2에서 에러 발생
open class SuperClass2{
    
    // constructor(){
    //     println("SuperClass2의 매개변수가 없는 생성자")
    // }
    constructor(a1:Int){
        println("SuperClass2의 매개변수가 있는 생성자")
    }
}

// SuperClass2를 상속받은 클래스
// 자식클래스는 부모클래스의 생성자를 반드시 호출해야 한다.
class SubClass2 : SuperClass2{
    constructor(){

    }
    constructor(a1:Int):super(100){

    }
}
  • 부모의 생성자 호출은 다음과 같이 작성해도 된다.
class SubClass3 : SuperClass2(100)

fun main(){
    val s5 = SubClass3()
    println("s5 : $s5")
}
SuperClass2의 매개변수가 있는 생성자
s5 : SubClass3@fa2bde7d

문제13-1

학생정보를 관리하는 프로그램을 작성한다.
학생은 총 6명이다.
농구부 학생은 학생의 이름, 부소속, 총 슛 개수로 구성된다.
축구부 학생은 학생의 이름, 부소속, 총 퇴장 개수로 구성된다.
야구부 학생은 학생의 이름, 부소속, 홈런 개수로 구성된다.

농구부 학생은 달린다, 슛을 쏜다라는 행동을 할 수 있다.
축구부 학생은 달린다, 퇴장 당한다라는 행동을 할 수 있다.
야구부 학생은 달린다, 홈런을 친다라는 행동을 할 수 있다.

프로그램이 시작되면 농구부 학생 두 명, 축구부 학생 두 명, 야구부 학생 두 명 순서대로 입력을 받는다.

입력시 부소속은 입력받지 않는다.

모든 학생의 정보가 입력이 완료되면
농구부 학생들에 대한 달린다와 슛을 쏜다라는 행동을 하게 하고
축구부 학생들에 대한 달린다와 퇴장 당한다라는 행동을 하게 하고
야구부 학생들에 대한 달린다와 홈런을 친다라는 행동을 하게 한다.

그 이후, 모든 학생들의 정보를 출력한다.

문제13-2

동물을 관리하는 프로그램을 작성한다.
동물은 총 6명이다.
호랑이는 이름, 동물종류, 다리 개수로 구성된다.
사자는 이름, 동물종류, 털 개수로 구성된다.
여우는 이름, 동물종류, 꼬리 개수로 구성된다.

호랑이는 먹는다, 달린다라는 행동을 할 수 있다.
사자는 먹는다, 염색한다라는 행동을 할 수 있다.
여우는 먹는다, 유혹한다라는 행동을 할 수 있다.

프로그램이 시작되면 호랑이 두마리, 사자 두마리, 여우 두마리 순서대로 입력을 받는다.

입력시 동물종류는 입력받지 않는다.

모든 동물의 정보가 입력이 완료되면
호랑이는 먹는다와 달린다라는 행동을 하게 하고
사자는 먹는다와 염색한다라는 행동을 하게 하고
여우는 먹는다와 유혹한다라는 행동을 하게 한다.

그 이후, 모든 동물들의 정보를 출력한다.




※ 출처 : 멋쟁이사자 앱스쿨 2기, 소프트캠퍼스 
profile
안드로이드공부

0개의 댓글