
강사님이 마지막에 오버라이딩 설명을 해주시는데 꽤 어려웠다...
다형성, 오버라이딩에 대한 why가 필요하다
추가로 코틀린에서 자바의 클래스를 가져올때에도 객체.필드 문법으로 코틀린처럼 getter, setter를 사용하는게 가능하다고 인프런의 다른 코틀린 강좌에서 설명한다




// 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의 메서드
// 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의 메서드

// 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.*
fun main(){
// pkg3 에 있는 모든 클래스들을 import 하였기 때문에 패키지명을 생략하고 사용할 수 있다.
val obj4 = TestClass3()
val obj5 = TestClass4()
obj4.t3Method()
obj5.t4Method()
}
TestClass3의 메서드입니다
TestClass4의 메서드입니다



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
// 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

// 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




// 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()
// 같은 파일의 클래스
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")
}
}
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
class TestClass1 {
public var value3 = 300
public val value4 = 400
}
// 자바 코드로 변환될 때
class TestClass1 {
private I value3
private final I value4
...
getter()
setter()
}
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
class TestClass1 {
var value7 = 700
set(value) {
if(value > 100){
field = value
}
}
}
class TestClass1 {
val value8 = 800
// set(value){ //setter는 에러남
//
// }
}
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
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 : 안녕하세요
class TestClass1{
val a6:Int
val a7:Int
init{
a6 = 600
}
constructor(){
a7 = 700
}
}
class TestClass1{
val a8:Int by lazy{
val temp = 1+2+3+4+5
// 마지막에 작성한 값이나 변수의 값이 property에 저장된다.
temp // 여기까지가 lazy구문임을 IntelliJ에서는 ^lazy로 표시해줌
}
}
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의 메서드
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
다음 시간에
다음 시간에
※ 출처 : 멋쟁이사자 앱스쿨 2기, 소프트캠퍼스