[Android/Flutter 교육] 7일차

MSU·2024년 1월 4일

Android-Flutter

목록 보기
7/85
post-thumbnail

강사님이 마지막에 오버라이딩 설명을 해주시는데 꽤 어려웠다...

다형성, 오버라이딩에 대한 why가 필요하다

추가로 코틀린에서 자바의 클래스를 가져올때에도 객체.필드 문법으로 코틀린처럼 getter, setter를 사용하는게 가능하다고 인프런의 다른 코틀린 강좌에서 설명한다

패키지

pakage

  • 작업하는 파일들을 폴더별로 나눠서 관리하는 개념
  • 물리적인 폴더를 만들어 사용한다.
  • 패키지는 폴더로 구분하면 된다
  • 패키지 내에 있는 파일들은 상단에 패키지를 반드시 명시해야 한다
  • 다른 패키지에 있는 동일한 명의 클래스를 구분해줘야 하기 때문
  • 패키지명은 전부 소문자로 구성되어야함(안드로이드에서는 패키지명에 대문자가 포함될 경우 에러남)



// TestClass1.kt
package kr.co.lion.pkg1

class TestClass1 {
    
    var t1 = 0
    
    fun t1Method(){
        println("TestClass1의 메서드")
    }
}


// main.kt
fun main() {
    // 다른 패키지의 클래스를 사용한다.
    // 사용하고자 하는 클래스와 똑같은 이름의 클래스가 여러 패키지에 있을 수도 있기 때문에
    // 내가 사용하고자 하는 클래스가 어떤 패키지에 있는 것인지를 명시해줘야 한다.
    val obj1 = kr.co.lion.pkg1.TestClass1()
    println("obj1.t1 : ${obj1.t1}")
    obj1.t1Method()
}
obj1.t1 : 0
TestClass1의 메서드

import

  • import를 사용해 패키지를 명시하면 코드 내에서 패키지 명을 생략할 수 있다.
// TestClass2.kt
package kr.co.lion.pkg1

class TestClass2 {
    var t2 = 1
    fun t2Method(){
        println("TestClass2의 메서드")
    }
}



// main.kt
import kr.co.lion.pkg1.TestClass2

fun main() {
	// TestClass2는 import로 명시하였기 때문에 패키지명을 생략할 수 있다.
    var obj2 = TestClass2()
    println("obj2.t2 : ${obj2.t2}")
    obj2.t2Method()
}
obj2.t2 : 1
TestClass2의 메서드
  • 다른 패키지에 동일한 이름의 클래스가 있는 경우
  • 패키지가 다르다고 하더라도 동일명의 클래스를 import해주는 것은 불가능하다.
    VM입장에서 클래스가 어떤 패키지에 있는 것인지 명확하지 않기 때문이다.
// pkg2 TestClass2.kt
package kr.co.lion.pkg2


class TestClass2 {
    var t22 = 22
    fun t22Method(){
        println("pkg2의 TestClass2의 메서드")
    }
}

// main.kt
import kr.co.lion.pkg1.TestClass2

// 패키지가 다르다고 하더라도 동일명의 클래스를 import해주는 것은 불가능하다
// VM입장에서 클래스가 어떤 패키지에 있는 것인지 명확하지 않기 때문이다.
// import kr.co.lion.pkg2.TestClass2 // 에러


fun main() {
    // pkg1의 TestClass2는 import로 명시하였기 때문에 패키지명을 생략할 수 있다.
    var obj2 = TestClass2()
    println("obj2.t2 : ${obj2.t2}")
    obj2.t2Method()

    // 이미 import 되어 있는 클래스 이름과 동일한 다른 패키지의 클래스를 사용할 때는
    // 패키지명을 생략할 수 없다.
    var obj3 = kr.co.lion.pkg2.TestClass2()
    println("obj3.t22 : ${obj3.t22}")
    obj3.t22Method()
}
obj3.t22 : 22
pkg2의 TestClass2의 메서드
  • 모든 클래스의 이름을 다르게 해주면 문제가 해결됨
  • 특정 패키지에 있는 모든 클래스를 import 하고 싶을 때 *를 사용한다.
    kr.co.lion.pkg3에 있는 모든 클래스들은 패키지명을 생략하고 사용할 수 있다.
    주의 : pkg3에 있는 클래스와 이름은 같지만 패키지가 다른 클래스를 import하게 되면 오류가 발생한다.
import kr.co.lion.pkg3.*

fun main(){
    // pkg3 에 있는 모든 클래스들을 import 하였기 때문에 패키지명을 생략하고 사용할 수 있다.
    val obj4 = TestClass3()
    val obj5 = TestClass4()
    obj4.t3Method()
    obj5.t4Method()
}
TestClass3의 메서드입니다
TestClass4의 메서드입니다
  • 패키지를 일일이 작성할 필요없이 개발도구(IntelliJ)에서 제공하는 자동완성 기능을 이용하면 편하게 import할 수 있음

모듈

  • kt파일들을 모아 관리하는 개념이 패키지라면 모듈은 패키지를 모아 관리하는 개념
  • 패키지가 많아지면 관리하기가 어려워질 수 있는데 이 때 모듈로 묶어서 사용한다
  • 안드로이드 앱은 스마트폰용 모듈, 워치용 모듈, 자동차용 모듈 등으로 구성되어 있음

접근제한자

  • 클래스, 메서드, 변수 등에 대한 접근 권한을 설정한다.
  • 사용자가 변수에 직접적으로 접근할 수 있게 되면 해당 변수를 자유롭게 사용할 수 있음
    자유롭게 사용하면서 발생하는 문제를 차단하기 위해 접근제한자를 사용
    접근제한자를 사용함으로써 변수에 직접 접근이 어려워지고 메서드를 이용하여 변수에 넣는 값이 이상없는지 미리 검사하고 판단하여 이상이 없는 경우에만 변수에 값을 넣어주는 방식으로 변수를 다룸.
  • 협업을 위해 접근제한자를 꼭 사용하는 버릇을 들이기

기본값

  • 생략하면 public으로 설정되고 internal의 경우 모듈이 같으면 public과 동일하다

클래스

  • private : 파일이 같을 경우에만 사용이 가능하다.
  • public : 다른 파일, 다른 패키지, 다른 모듈에서도 사용이 가능하다. (기본)
  • internal : 모듈이 다르면 사용이 불가능하다.
  • 클래스에는 protected를 붙일 수 없다.

클래스 - 같은 모듈, 같은 패키지, 같은 파일

  • 같은 파일에 있는 클래스 사용은 접근 제한자에 관계없이 모두 사용이 가능하다.
fun main() {
    // 같은 파일에 있는 클래스 사용
    // 접근 제한자에 관계없이 모두 사용이 가능하다.
    val obj10 = PrivateClass1()
    val obj11 = PublicClass1()
    val obj12 = InternalClass1()
    println("obj10 : $obj10")
    println("obj11 : $obj11")
    println("obj12 : $obj12")
}
obj10 : PrivateClass1@aaa655d8
obj11 : PublicClass1@aa81f031
obj12 : InternalClass1@f3cc7a36

클래스 - 같은 모듈, 같은 패키지, 다른 파일

  • private 사용 불가
// TestFile1.kt
// 같은 모듈, 같은 패키지, 다른 파일
private class PrivateClass2
public class PublicClass2
internal class InternalClass2


// main.kt
fun main() {
    // 같은 모듈, 같은 패키지, 다른 파일
    
    // private는 파일이 다르면 사용이 불가
    // val obj20 = PrivateClass2() // 에러남
    // public은 무조건 사용 가능
    val obj21 = PublicClass2()
    // internal은 모듈이 같다면 public과 동일
    val obj22 = InternalClass2()
    
    // println("obj20 : $obj20")
    println("obj21 : $obj21")
    println("obj22 : $obj22")
}
obj21 : PublicClass2@1e9d47a7
obj22 : InternalClass2@5a0aa83b


PrivateClass2() 부분 실행 시 에러
Kotlin: Cannot access 'PrivateClass2': it is private in file

클래스 - 같은 모듈, 다른 패키지, 다른 파일

  • private 사용 불가
// kr.co.lion.pkg1 / TestFile2.kt
// 같은 모듈, 다른 패키지
package kr.co.lion.pkg1

private class PrivateClass3
public class PublicClass3
internal class InternalClass3


// main.kt
import kr.co.lion.pkg1.*

fun main() {
    // 같은 모듈, 다른 패키지
    
    // 파일이 다르므로 PrivateClass3 사용 불가
    // val obj30 = PrivateClass3() // 에러남
    val obj31 = PublicClass3()
    val obj32 = InternalClass3()
    
    println("obj31 : $obj31")
    println("obj32 : $obj32")
}
obj31 : kr.co.lion.pkg1.PublicClass3@ca9cded1
obj32 : kr.co.lion.pkg1.InternalClass3@fc62e7e0


PrivateClass3() 부분 실행 시 에러
Kotlin: Cannot access 'PrivateClass3': it is private in file

클래스 - 다른 모듈, 다른 패키지, 다른 파일

  • private, internal 사용 불가
  • 원래 자바에서는 모듈 개념이 없었음, 개발도구에서 지원해주고 있음
  • 다른 모듈을 사용하기 위한 세팅 필요



// SecondModule / kr.co.lion.pkg2 / TestFile3.kt
// 다른 모듈
package kr.co.lion.pkg2

private class PrivateClass4
public class PublicClass4
internal class InternalClass4


// main.kt
import kr.co.lion.pkg2.*

fun main() {
    // 다른 모듈
    // val obj40 = PrivateClass4()
    val obj41 = PublicClass4()
    // Internal은 모듈이 다르면 사용이 불가능하다
    // val obj42 = InternalClass4()
    
    println("obj41 : $obj41")
}
obj41 : kr.co.lion.pkg2.PublicClass4@d6a38e7e

클래스 - 상속할 때

  • 클래스는 접근제한자가 같은 것 끼리만 상속이 가능
  • 상속 시 자식 클래스는 부모클래스의 접근 제한자를 따라야 한다.
  • 같은 파일에 있는 모든 클래스들은 상속이 자유롭다.

같은 파일의 클래스 상속

// main.kt
// 같은 파일에 있는 클래스
open private class PrivateClass1
open public class PublicClass1
// 클래스에는 protected 를 붙일 수 없다.
// protected class ProtectedClass1
open internal class InternalClass1


// 같은 파일에 있는 클래스 상속
// 상속시 자식 클래스는 부모클래스의 접근 제한자를 따라야한다.
// 같은 파일에 있는 모든 클래스들은 상속이 자유롭다
private class TestClass1 : PrivateClass1()
public class TestClass2 : PublicClass1()
internal class TestClass3 : InternalClass1()

같은 모듈, 같은 패키지, 다른 파일의 클래스 상속

// TestFile1.kt
// 같은 모듈, 같은 패키지, 다른 파일
open private class PrivateClass2
open public class PublicClass2
open internal class InternalClass2


// main.kt
// 같은 모듈, 같은 패키지, 다른 파일의 클래스 상속
// private class TestClass4 : PrivateClass2()
public class TestClass5 : PublicClass2()
internal class TestClass6 : InternalClass2()

같은 모듈, 다른 패키지의 클래스 상속

// kr.co.lion.pkg1 / TestFile2.kt
// 같은 모듈, 다른 패키지
package kr.co.lion.pkg1

open private class PrivateClass3
open public class PublicClass3
open internal class InternalClass3



// main.kt
import kr.co.lion.pkg1.*
// 같은 모듈, 다른 패키지
// private class TestClass7 : PrivateClass3()
public class TestClass8 : PublicClass3()
internal class TestClass9 : InternalClass3()

다른 모듈의 클래스 상속

// SecondModule / kr.co.lion.pkg2 / TestFile3.kt
// 다른 모듈
package kr.co.lion.pkg2

open private class PrivateClass4
open public class PublicClass4
open internal class InternalClass4



// main.kt
import kr.co.lion.pkg2.*
// 다른 모듈
// private class TestClass10 : PrivateClass4()
public class TestClass11 : PublicClass4()
// internal class TestClass12 : InternalClass4()

변수, 메서드

  • private : 모든 경우에 사용이 불가능하다
  • public : 모든 경우에 사용이 가능하다. (기본)
  • protected : 상속 관계에서만 사용이 가능하다. 패키지, 모듈이 달라도 사용 가능하다.
  • internal : 모듈이 다르면 사용이 불가능하다.

변수, 메서드 - 같은 파일

// 같은 파일의 클래스
open class SuperClass1{
    private var a10 = 100
    public var a11 = 101
    protected var a12 = 102
    internal var a13 = 103
}

fun main(){
    // 같은 파일에 있는 클래스의 객체를 생성하여 변수나 메서드에 접근한다.
    val obj50 = SuperClass1()
    // private는 절대 사용 불가
    // println("obj50.a10 : ${obj50.a10}")
    println("obj50.a11 : ${obj50.a11}")
    // protected 객체 생성을 통한 사용에는 접근이 불가
    // println("obj50.a12 : ${obj50.a12}")
    println("obj50.a13 : ${obj50.a13}")
}

// 같은 파일의 클래스 상속
class SubClass1 : SuperClass1(){
    fun subMethod1(){
    	// private는 절대 사용 불가
        // println("a10 : $a10")
        println("a11 : $a11")
        // protected는 상속 관계에서는 사용 가능
        println("a12 : $a12")
        println("a13 : $a13")
    }
}
obj50.a11 : 101
obj50.a13 : 103

변수, 메서드 - 같은 모듈, 같은 패키지, 다른 파일

// TestFile1.kt
// 같은 모듈, 같은 패키지, 다른 파일
open private class PrivateClass2
open public class PublicClass2
open internal class InternalClass2

open class SuperClass2{
    private var a20 = 100
    public var a21 = 200
    protected var a22 = 300
    internal var a23 = 400
}

// main.kt
fun main(){
    // 같은 모듈, 같은 패키지, 다른 파일
    var obj60 = SuperClass2()
    // println("obj60.a20 : ${obj60.a20}")
    println("obj60.a21 : ${obj60.a21}")
    // 상속관계가 아니기 때문에 접근 불가
    // println("obj60.a22 : ${obj60.a22}")
    println("obj60.a23 : ${obj60.a23}")
}

// 같은 모듈, 같은 패키지, 다른 파일
class SubClass2 : SuperClass2(){

    fun subMethod2(){
        // println("a20 : $a20")
        println("a21 : $a21")
        println("a22 : $a22")
        println("a23 : $a23")
    }
}

변수, 메서드 - 같은 모듈, 다른 패키지

// kr.co.lion.pkg1 / TestFile2.kt
// 같은 모듈, 다른 패키지
package kr.co.lion.pkg1

open private class PrivateClass3
open public class PublicClass3
open internal class InternalClass3

open class SuperClass3{

    private var a30 = 100
    public var a31 = 200
    protected var a32 = 300
    internal var a33 = 400

}




// main.kt
fun main(){
    // 같은 모듈, 다른 패키지
    var obj70 = SuperClass3()
    // println("obj60.a30 : ${obj70.a30}")
    println("obj60.a31 : ${obj70.a31}")
    // println("obj60.a32 : ${obj70.a32}")
    println("obj60.a33 : ${obj70.a33}")
}

// 같은 모듈, 다른 패키지
class SubClass3 : SuperClass3(){

    fun subMethod3(){
        // println("a30 : $a30")
        println("a31 : $a31")
        println("a32 : $a32")
        println("a33 : $a33")
    }
}

변수, 메서드 - 다른 모듈


// SecondModule / kr.co.lion.pkg2 / TestFile3.kt
package kr.co.lion.pkg2
// 다른 모듈

open private class PrivateClass4
open public class PublicClass4
open internal class InternalClass4

open class SuperClass4{
    private var a40 = 100
    public var a41 = 200
    protected var a42 = 300
    internal var a43 = 400
}




// main.kt
fun main(){
    // 다른 모듈
    var obj80 = SuperClass4()
    // private는 무조건 못씀
    // println("obj80.a40 : ${obj80.a40}")
    println("obj80.a41 : ${obj80.a41}")
    // protected는 상속관계
    // println("obj80.a42 : ${obj80.a42}")
    // internal은 같은 모듈
    // println("obj80.a43 : ${obj80.a43}")
}


// 다른 모듈
class SubClass4 : SuperClass4(){

    fun subMethod4(){
    	// private는 무조건 못씀
        // println("a40 : $a40")
        println("a41 : $a41")
        // 상속관계라 protected 사용 가능
        println("a42 : $a42")
        // internal은 같은 모듈만
        // println("a43 : $a43")
    }
}

Property

  • 객체가 관리하는 멤버변수
  • 캡슐화를 적용한 변수
  • private 접근 제한자를 설정한 변수
  • 캡슐화를 적용한 변수(private 접근 제한자를 설정한 변수)에 값을 저장할 수 있는 메서드(setter)나 변수의 값을 반환하는 메서드(getter)를 제공하는 것

캡슐화

  • 객체가 가지고 있는 변수는 메서드에 구현한 코드가 정상적으로 동작하기 위한 데이터를 담고 있다.
  • 만약 변수에 엉뚱한 데이터가 담긴다면 구현한 코드가 정상적으로 동작한다고 하더라도 정상적인 결과가 나올 수 없게 된다.
  • 이에 변수의 직접적인 접근을 차단하여 외부에서 값을 변경하지 못하도록 막는 것을 캡슐화라고 한다.
  • 캡슐화가 적용된 변수는 외부에서의 접근이 차단됨
  • 캡슐화가 적용된 변수에 대해 메서드를 통해 값을 설정하거나 가져다 사용할수 있도록 구성한 것을 property라고 부른다
  • 변수에 값을 넣는 메서드를 setter라고 부른다
  • 변수가 가지고 있는 값을 반환하는 메서드를 getter라고 부른다

Property 작성 방법

  • private키워드를 사용하여 외부로의 접근을 막는다.

fun main() {
    val t1 = TestClass1()
    // 캡슐화를 적용한 변수는 접근이 불가능하다.
    // println("t1.value1 : ${t1.value1}")
    // println("t1.value2 : ${t1.value2}")

    println("t1.value2 : ${t1.getValue2()}")
    t1.setValue2(2000)
    println("t1.value2 : ${t1.getValue2()}")

	// 0이상의 값만 저장해주는 메서드이기 때문에 value2의 값이 바뀌지 않음
    // 데이터의 무결성 보장
    t1.setValue2(-2000)
    println("t1.value2 : ${t1.getValue2()}")
}

class TestClass1 {
    //캡슐화를 적용한 변수들
    private var value1 = 100
    private var value2 = 200

    // 변수에 값을 저장하는 setter
    // 변수에 접근이 불가능하기 때문에 값을 직접 저장하는 것은 불가능
    // 이에 setter를 통해 값을 저장해야 하는데 메서드이므로 코드 작성이 가능하다.
    // 이를 통해 변수에 저장될 값의 제약 조건을 설정할 수 있다.
    public fun setValue2(value2:Int){
        if(value2 > 0){
            this.value2 = value2
        }
    }
    // 변수에 저장되어 있는 값을 반환하는 getter
    public fun getValue2():Int{
        return value2
    }
}
t1.value2 : 200
t1.value2 : 2000
t1.value2 : 2000

코틀린에서의 property

  • 코틀린에서 private가 아닌 접근 제한자를 붙인 변수는 Java로 변환될 때 변수의 접근 제한자는 private로 변경된다.
    이때, var로 선언된 변수는 setter와 getter가 추가된다.
    val로 선언된 변수는 getter만 추가된다.
  • 자바에서는 setter와 getter를 직접 추가해주었으나 코틀린에서는 내용을 수정하지 않는 한 직접 추가해줄 필요가 없다
class TestClass1 {
    public var value3 = 300
    public val value4 = 400
}


// 자바 코드로 변환될 때
class TestClass1 {
    private I value3
    private final I value4
    
    ... 
    
    getter()
    setter()
}
  • 자동 생성되는 setter는 매개변수의 값을 그냥 멤버 변수에 저장하는 코드로 되어있다.
    자동 생성되는 getter는 멤버 변수의 값을 반환하는 코드로 되어 있다.
    만약 자동 생성되는 setter나 getter를 사용하지 않고 직접 정의하고 싶다면 다음과 같이 작성해준다.
class TestClass1 {
    public var value3 = 300
    public val value4 = 400

    var value5 = 500
    	// field : 변수를 의미
    	// getter
        get(){
            println("getter 호출")
            return field
        }
        // setter
        set(value){
            println("setter 호출")
            if(value > 0){
                field = value
            }
        }
    
    var value6 = 600
        get() {
            return field
        }
        set(value) {
            if(value > 50){
                field = value
            }
        }
}

fun main(){
    // property 사용
    t1.value3 = 3000
    t1.value5 = 5000
    println("t1.value3 : ${t1.value3}")
    println("t1.value4 : ${t1.value4}")
    println("t1.value5 : ${t1.value5}")

    t1.value6 = 6000
    println("t1.value6 : ${t1.value6}")
}
setter 호출
t1.value3 : 3000
t1.value4 : 400
getter 호출
t1.value5 : 5000
t1.value6 : 6000
  • var property는 setter/getter가 모두 필요하지만
    setter만 정의할 경우 getter는 기본 코드로 추가된다.
class TestClass1 {
    var value7 = 700
        set(value) {
            if(value > 100){
                field = value
            }
        }
}
  • val property는 getter만 정의한다.
class TestClass1 {
    val value8 = 800
    //    set(value){ //setter는 에러남
	//
    //    }
}

지연초기화

  • 코틀린은 프로퍼티를 정의하면 무조건 값을 저장해야 한다.
    이는 개발자가 값을 저장하지도 않았는데 프로퍼티를 사용하려고 하는 것을 예방하기 위함인데
    코드를 수행시켜서 얻어진 값을 저장해야 하는 경우 프로퍼티에 일단 아무 값이나 넣어두고 나중에 얻어진 값을 저장하도록 해야 한다.
  • 지연 초기화를 이용하면 프로퍼티를 정의할 때 저장할 값을 지정하지 않고 나중에 지정해도 된다.

Property 정의 시 예외 상황

  • Property 정의 시 저장할 값을 지정하지 않았지만 init블럭이나 생성자에서 값을 저장하는 코드를 작성해두면 오류가 발생하지 않는다.
  • 이는 객체를 생성하면 무조건 init블럭과 생성자의 코드가 동작하기 때문에 Property에 값이 저장되는 것을 보장받을 수 있기 때문이다.
fun main() {
    val t1 = TestClass1()
    println("t1.a1 : ${t1.a1}")
    println("t1.a2 : ${t1.a2}")
    println("t1.a3 : ${t1.a3}")
    println("t1.a4 : ${t1.a4}")
}

class TestClass1{
    // kotlin에서는 Property를 정의할 때 저장할 값을 무조건 지정해야 한다.
    var a1:Int = 100
    var a2 = 200
    
    // Property 정의 시 저장할 값을 지정하지 않았지만 init블럭이나 생성자에서 값을 저장하는 코드를 작성해두면
    // 오류가 발생하지 않는다.
    // 이는 객체를 생성하면 무조건 init블럭과 생성자의 코드가 동작하기 때문에
    // Property에 값이 저장되는 것을 보장받을 수 있기 때문이다.
    var a3:Int
    var a4:Int

    init{
        a4 = 400
    }

    constructor(){
        a3 = 100
    }
}
t1.a1 : 100
t1.a2 : 200
t1.a3 : 100
t1.a4 : 400

lateinit

  • var로 선언된 변수의 초기화를 뒤로 미룰 수 있다.
  • 변수의 값을 사용하기 전에 반드시 초기화가 이루어져야 한다.
  • val로 선언된 변수는 사용 불가
  • init블럭이나 생성자에서 값을 저장하지 않고 메서드에서 값을 저장하는 경우 사용한다.
  • Int, Float, Double과 같은 기본 자료형은 사용할 수 없다.
fun main(){
	val t1 = TestClass1()
    t1.testMethod1()
}


class TestClass1{
    // 지연 초기화
    // init블럭이나 생성자에서 값을 저장하지 않고 메서드에서 값을 저장하는 경우 사용한다.
    // var 변수만 가능하다.
    // Int, Float, Double과 같은 기본 자료형은 사용할 수 없다.
    lateinit var a5:String

    fun testMethod1(){
        // lateinit property는 사용전에 반드시 초기화 되었는지를 검사하는 것이 좋다.
        if(::a5.isInitialized){
            println("step1 : $a5") // 이 코드는 실행되지 않음
        }
        // lateinit property에 값을 저장한다.
        a5 = "안녕하세요"
        if(::a5.isInitialized){
            println("step2 : $a5")
        }
    }
}
step2 : 안녕하세요

lazy

  • val 프로퍼티도 init블럭과 생성자로 나중에 초기화 가능
class TestClass1{
    val a6:Int
    val a7:Int
    
    init{
        a6 = 600
    }

    constructor(){
        a7 = 700
    }
}
  • lazy를 사용하면 property에 저장될 값을 어떠한 처리를 통해 저장할 수 있다.
class TestClass1{
    val a8:Int by lazy{
        val temp = 1+2+3+4+5
        // 마지막에 작성한 값이나 변수의 값이 property에 저장된다.
        temp // 여기까지가 lazy구문임을 IntelliJ에서는 ^lazy로 표시해줌
    }
}

Overriding

  • 꼭 Overriding의 개념을 이해해야 안드로이드를 할 수 있다.
  • 상속 관계에서의 개념

다형성

  • 어떠한 요소의 형태가 다양할 수 있다는 의미
  • 객체를 생성하면 객체의 주소값이 반환되는데 이 주소값은 부모클래스형 변수에도 담을 수 있다.
fun main() {
    // 객체 생성 후 객체 생성시 사용한 클래스 타입의 변수에 주소값을 저장한다.
    val s1:SubClass1 = SubClass1()

    // 객체 생성 후 부모 클래스 타입의 변수에 주소값을 저장한다.
    // SubClass1이 SuperClass1을 상속받지 않았다면 이 코드는 에러가 남
    val s2:SuperClass1 = SubClass1()

    println("s1 : $s1")
    println("s2 : $s2")
    
    // 객체를 생성했을 때 사용한 클래스 타입의 변수를 통해 객체에 접근한다.
    // 부모 영역 사용 가능
    println("s1.superValue1 : ${s1.superValue1}")
    s1.superMethod1()
    
    // 자기 자신에게 작성된 요소를 사용한다.
    // s1 변수는 객체를 생성했을 때 사용한 클래스 타입의 변수이므로 모든 것을 사용할 수 있다.
    println("s1.subValue1 : ${s1.subValue1}")
    s1.subMethod1()

    // 부모클래스 타입의 변수를 통해 객체에 접근한다.
    // 부모 영역 사용 가능
    println("s2.superValue2 : ${s2.superValue1}")
    s2.superMethod1()
    
    // s2는 부모클래스 타입으로 정의되어 있지만 담겨있는 객체의 주소 값은
    // 자식클래스 타입의 주소값이 들어있다.
    // 허나, 변수의 타입만 보고 프로퍼티나 메서드 존재 여부를 확인하기 때문에
    // 부모클래스형 변수를 통해서 자식클래스에 정의된 요소를 사용할 수 없다.
    // 이는 부모클래스형 변수에 부모클래스를 통해 만든 객체의 주소값이 들어있을 가능성이 있기 때문이다.
    // println("s2.subValue1 : ${s2.subValue1}")
    // s2.subMethod1()
}

open class SuperClass1{
    
    val superValue1 = 100
    
    fun superMethod1(){
        println("SuperClass1의 메서드")
    }
}

class SubClass1 : SuperClass1(){
    val subValue1 = 200

    fun subMethod1(){
        println("SubClass1의 메서드")
    }
}
s1 : SubClass1@e19cd226
s2 : SubClass1@cad4e604

s1.superValue1 : 100
SuperClass1의 메서드

s1.subValue1 : 200
SubClass1의 메서드

s2.superValue2 : 100
SuperClass1의 메서드

Overriding

  • 부모가 가지고 있는 메서드를 다시 구현하는 개념
  • 매개변수의 형태, 이름 등등 모든 것이 같아야 한다.
fun main() {
    val s1:SubClass1 = SubClass1()
    val s2:SuperClass1 = SubClass1()

    s1.superMethod2()
    s2.superMethod2()
}

open class SuperClass1{
    
    val superValue1 = 100
    
    fun superMethod1(){
        println("SuperClass1의 메서드")
    }

    // 자식클래스에서 overriding하는 것을 허용하겠다면 open을 붙여준다.
    open fun superMethod2(){
        println("SuperClass1의 SuperMethod2")
    }
}

class SubClass1 : SuperClass1(){
    val subValue1 = 200

    fun subMethod1(){
        println("SubClass1의 메서드")
    }

    // overriding : 부모가 가지고 있는 메서드를 다시 구현하는 개념
    // 매개변수의 형태, 이름 등등 모든 것이 같아야 한다.
    // overriding한 메서드는 override 키워드를 붙여줘야 한다.
    override fun superMethod2(){
        println("SubClass1의 superMethod2")
    }
}
SubClass1의 superMethod2
SubClass1의 superMethod2
  • s1의 타입은 SubClass1이고, s2의 타입은 SuperClass1인데 둘 다 superMethod2를 호출하면 SubClass1의 superMethod2가 호출이된다.

Any

다음 시간에

this와 super

다음 시간에




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

0개의 댓글