Kotlin Study 06_1
IDE : Intellij
JDK : zulu11
클래스와 객체
객체 지향 프로그래밍
용어
- 추상화(Abstraction)
: 특정 클래스를 만들 때 기본 형식을 규정하는 방법
- 인스턴스(Instance)
: 클래스로부터 생성한 객체
- 상속(Inheritance)
: 부모 클래스의 내용을 자식 클래스가 그대로 물려받음
- 다형성(Polymorphism)
: 하나의 이름으로 다양한 처리를 제공
- 캡슐화(Encapsulation)
: 내용을 숨기고 필요한 부분만 사용
- 메시지 전송(Message Sending)
: 객체 간에 주고받는 메시지
- 연관(Association)
: 클래스 간의 관계
In Kotlin
: 특정 클래스를 만들 때 기본 형식을 규정하는 방법
: 클래스로부터 생성한 객체
: 부모 클래스의 내용을 자식 클래스가 그대로 물려받음
: 하나의 이름으로 다양한 처리를 제공
: 내용을 숨기고 필요한 부분만 사용
: 객체 간에 주고받는 메시지
: 클래스 간의 관계
Kotlin | Other Language |
---|---|
Class | - |
Property | Attribute, Variable, Field, Data |
Method | Function, Operation, Behavior |
Object | Instance |
클래스와 추상화
BirdClassDefine.kt
class Bird { // 클래스 정의
// 프로퍼티
var name: String = "mybird"
var wing: Int = 2
var beak: String = "short"
var color: String = "blue"
// 메서드
fun fly() = println("Fly wing: $wing")
fun sing(vol: Int) = println("Sing vol: $vol")
}
fun main() {
val coco = Bird() // 객체 생성
coco.color = "blue" // 객체 프로퍼티에 값 할당
println("coco.color: ${coco.color}") // 객체 프로퍼티 읽기
coco.fly() // 객체 메서드 사용
coco.sing(3)
}
객체 생성 및 사용
생성자
class 클래스이름 constructor(매개변수) { // 주 생성자
...
constructor(매개변수){ // 부 생성자
// 프로퍼티 초기화
}
...
}
객체가 생성될 때 기본적으로 호출되는 함수
BirdSecondaryConstructor.kt
class Bird{ // 클래스 정의
var name: String
var wing: Int
var beak: String
var color: String
constructor(_name: String, _wing: Int, _beak: String, _color: String){ // 제 1 부 생성자
this.name = _name
this.wing = _wing
this.beak = _beak
this.color = _color
}
constructor(_name: String, _beak: String){ // 제 2 부 생성자
this.name = _name
this.wing = 2
this.beak = _beak
this.color = "grey"
}
// 메서드
fun fly() = println("Fly wing: $wing")
fun sing(vol: Int) = println("Sing vol: $vol")
}
fun main() {
val bird1 = Bird("mybird", 2, "short", "blue")
val bird2 = Bird("mybird2", "long")
// bird1
println("bird1.name: ${bird1.name}")
println("bird1.wing: ${bird1.wing}")
println("bird1.beak: ${bird1.beak}")
println("bird1.color: ${bird1.color}")
bird1.fly()
println()
// bird2
println("bird2.name: ${bird2.name}")
println("bird2.wing: ${bird2.wing}")
println("bird2.beak: ${bird2.beak}")
println("bird2.color: ${bird2.color}")
bird2.fly()
주 생성자 없이 부 생성자를 여러개 가지는 Bird 클래스
실행결과
bird1.name: mybird bird1.wing: 2 bird1.beak: short bird1.color: blue Fly wing: 2 bird2.name: mybird2 bird2.wing: 2 bird2.beak: long bird2.color: grey Fly wing: 2
BirdPrimaryConstructor.kt
class Bird(
var name: String,
var wing: Int,
var beak: String,
var color: String
) {
// 프로퍼티는 매개변수 안에 var를 사용해 프로퍼티로서 선언됨
// 메서드
fun fly() = println("Fly wing: $wing")
fun sing(vol: Int) = println("Sing vol: $vol")
}
fun main() {
val coco = Bird("mybird", 2, "short", "blue")
coco.color = "yello"
println("coco.color: ${coco.color}")
coco.fly()
coco.sing(3)
}
주 생성자의 매개변수에 프로퍼티가 선언되었으므로 본문에서 프로퍼티 선언이 생략된다.
실행결과
coco.color: yello Fly wing: 2 Sing vol: 3
BirdPrimaryInit.kt
class Bird(
var name: String,
var wing: Int,
var beak: String,
var color: String
) {
init {
println("----- 초기화 블록 시작 -----")
println("이름은 $name, 부리는 $beak")
this.sing(3)
println("----- 초기화 블록 끝 -----")
}
// 메서드
fun fly() = println("Fly wing: $wing")
fun sing(vol: Int) = println("Sing vol: $vol")
}
fun main() {
val coco = Bird("mybird", 2, "short", "blue")
coco.color = "yello"
println("coco.color: ${coco.color}")
coco.fly()
}
init 초기화 블록에서 출력문, 프로퍼티, 메서드 등과 같은 코드를 사용할 수 있다.
실행 결과
----- 초기화 블록 시작 ----- 이름은 mybird, 부리는 short Sing vol: 3 ----- 초기화 블록 끝 ----- coco.color: yello Fly wing: 2
프로퍼티 기본값 지정
class Bird(
var name: String = "NONAME", // name의 기본값 "NONAME"
var wing: Int = 2, // wing의 기본값 2
var beak: String,
var color: String
) {
// 메서드
fun fly() = println("Fly wing: $wing")
fun sing(vol: Int) = println("Sing vol: $vol")
}
fun main() {
val coco = Bird(beak = "long", color = "red")
println("coco.name: ${coco.name}, coco.wing: ${coco.wing}")
println("coco.color: ${coco.color}, coco.beak: ${coco.beak}")
}
생성자 매개변수에 기본값을 지정할 수 있다.
coco.name: NONAME, coco.wing: 2 coco.color: red, coco.beak: long
상속
BirdChildClassed.kt
// open 키워드로 상속 가능한 클래스 선언
open class Bird(
var name: String,
var wing: Int,
var beak: String,
var color: String
) {
// 메서드
fun fly() = println("Fly wing: $wing")
fun sing(vol: Int) = println("Sing vol: $vol")
}
// 주 생성자를 사용하는 상속
class Lark(
name: String,
wing: Int,
beak: String,
color: String
) : Bird(name, wing, beak, color){
// 새로 추가한 메소드
fun singHitone() = println("Happy Song!")
}
// 부 생성자를 사용하는 상속
class Parrot : Bird{
val language: String
constructor(
name: String,
wing: Int,
beak: String,
color: String,
language: String
) : super(name, wing, beak, color) {
// 새로 추가한 프로퍼티
this.language = language
}
fun speak() = println("Speak! $language")
}
fun main() {
val coco = Bird("mybird", 2, "short", "blue")
val lark = Lark("mylark", 2, "long", "brown")
val parrot = Parrot("myparrot", 2, "short", "multiple", "korean")
println("Coco: ${coco.name}, ${coco.wing}, ${coco.beak}, ${coco.color}")
println("Lark: ${lark.name}, ${lark.wing}, ${lark.beak}, ${lark.color}")
println("Parrot: ${parrot.name}, ${parrot.wing}, ${parrot.beak}, ${parrot.color}, ${parrot.language}")
lark.singHitone()
parrot.speak()
lark.fly()
}
open 키워드로 상속 가능한 클래스를 선언하고, 파생 클래스에서 상속하여 사용할 수 있다.
실행결과
Coco: mybird, 2, short, blue Lark: mylark, 2, long, brown Parrot: myparrot, 2, short, multiple, korean Happy Song! Speak! korean Fly wing: 2
다형성
OverloadCalc.kt
class Calc {
fun add(x: Int, y: Int): Int = x + y
fun add(x: Double, y: Double): Double = x + y
fun add(x: Int, y: Int, z: Int): Int = x + y + z
fun add(x: String, y: String): String = x + y
}
fun main() {
val calc = Calc()
println(calc.add(3, 2))
println(calc.add(3.2, 1.3))
println(calc.add(3, 3, 2))
println(calc.add("Hello", "World"))
}
다양한 매개변수로 오버로딩된 add 메서드
실행 결과
5 4.5 8 HelloWorld
BirdOverridingEx.kt
open class Bird(
var name: String,
var wing: Int,
var beak: String,
var color: String
) {
fun fly() = println("Fly wing: $wing")
open fun sing(vol: Int) = println("Sing vol: $vol") // 오버라이딩 가능한 메서드
}
class Parrot(
name: String,
wing: Int = 2,
beak: String,
color: String,
var language: String = "natural"
) : Bird(name, wing, beak, color) {
fun speak() = println("Speak! $language")
override fun sing(vol: Int) {
println("I'm a parrot! The volume level is $vol")
speak()
}
}
fun main() {
val parrot = Parrot(name = "myparrot", beak = "short", color = "multiple")
parrot.language = "English"
println("Parrot: ${parrot.name}, ${parrot.wing}, ${parrot.beak}, ${parrot.color}, ${parrot.language}")
parrot.sing(5)
}
기반 클래스에서 open 키워드, 파생 클래스에서 override 키워드를 사용하여 메서드 또는 프로퍼티를 오버라이딩 할 수 있다.
파생 클래스에서 더이상 메소드의 오버라이딩을 막고자 한다면, override 키워드 앞에 final 키워드를 사용하여 재정의를 막을 수 있다.
super와 this의 참조
super, this 키워드 이용
super | this |
---|---|
super.{프로퍼티 이름} | this.{프로퍼티 이름} |
super.{메서드 이름}() | this.{메서드 이름}() |
super() | this() |
상위 클래스는 super 키워드, 현재 클래스는 this 키워드로 참조 가능하다.
PersonThisSuper.kt
open class Person {
constructor(firstName: String){
println("[Person] firstName: $firstName")
}
constructor(firstName: String, age: Int){ // 3
println("[Person] firstName: $firstName, $age")
}
}
class Developer: Person {
constructor(firstName: String): this(firstName, 10) { // 1
println("[Developer] $firstName")
}
constructor(firstName: String, age: Int): super(firstName, age) { // 2
println("[Developer] $firstName, $age")
}
}
fun main() {
val sean = Developer("Sean")
}
main의
Developer("sean")
으로 Developer의 1번 생성자를 진입하며,this(firstName, 10)
에 의해 2번 생성자를 호출한다. 여기서 다시super(firstName, age)
에 의해 상위 클래스인 Person의 3번 생성자를 호출한다.실행 결과
[Person] firstName: Sean, 10 [Developer] Sean, 10 [Developer] Sean
PersonPriSeconRef.kt
class Person(
firstName: String,
out: Unit = println("[Primary Constructor] Parameter")
) { // 2. 주 생성자
val fName = println("[Property] Person fName: $firstName") // 3. 프로퍼티 할당
init {
println("[init] Person init block") // 4. 초기화 블록
}
// 1. 부 생성자
constructor(
firstName: String, age: Int,
out: Unit = println("[Secondary Constructor] Parameter")
) : this(firstName) {
println("[Secondary Constructor] Body: $firstName, $age") // 5. 부 생성자 본문
}
}
fun main() {
val p1 = Person("Kildong", 30) // 1 -> 2 호출 | 3 -> 4 -> 5 실해
println()
val p2 = Person("Dooly") // 2 호출 | 3 -> 4 실행
}
부 생성자에서 this를 사용해 주 생성자를 호출할 수 있다.
실행 결과
[Secondary Constructor] Parameter [Primary Constructor] Parameter [Property] Person fName: Kildong [init] Person init block [Secondary Constructor] Body: Kildong, 30 [Primary Constructor] Parameter [Property] Person fName: Dooly [init] Person init block
InnerClassRef.kt
open class Base {
open val x: Int = 1
open fun f() = println("Base Class f()")
}
class Child : Base() {
override val x: Int = super.x + 1
override fun f() = println("Child Class f()")
inner class Inside {
fun f() = println("Inside Class f()")
fun test() {
f() // Inside의 f()
Child().f() // Child의 f()
super@Child.f() // Base의 f()
println("[Inside] [email protected]: ${super@Child.x}")
}
}
}
fun main() {
val c1 = Child()
c1.Inside().test()
}
Inner Class에서 Outer Class의 Super Class를 호출하려면 super 키워드와 함께 @기호 옆에 Outer Class의 이름을 작성해야 한다.
실행 결과
Inside Class f() Child Class f() Base Class f() [Inside] [email protected]: 1
AngleBracketTest.kt
open class A {
open fun f() = println("A Class f()")
fun a() = println("A Class a()")
}
interface B {
fun f() = println("B Interface f()")
fun b() = println("B Interface b()")
}
class C : A(), B {
override fun f() = println("C Class f()")
fun test() {
f() // C의 f()
b() // B의 b()
super<A>.f() // A의 f()
super<B>.f() // B의 f()
}
}
fun main() {
val c = C()
c.test()
}
Kotlin에서는 Java처럼 다중 상속이 되지 않는다. 하지만 인터페이스로 필요한 만큼 다수의 인터페이스를 지정해 구현할 수 있다.
동일한 이름의 프로퍼티나 메서드가 있다면 앵글 브래킷(<>) 을 사용하여 접근하려는 클래스나 인터페이스 이름을 정하여 사용할 수 있다.
캡슐화
가시성 지시자
- private : 이 요소는 외부에서 접근할 수 없다.
- public : 이 요소는 어디서든 접근이 가능하다. (default)
- protected : 외부에서 접근할 수 없으나 하위 상속 요소에서는 가능하다.
- internal : 같은 정의의 모듈 내부에서는 접근이 가능하다.
주 생성자 앞에 가시성 지시자를 사용하는 경우, constructor
키워드를 생략할 수 없다.
InternalTest.kt
internal class InternalClass {
internal var i = 1
internal fun icFunc() {
i += 1 // 접근허용
}
fun access() {
icFunc() // 접근허용
}
}
class Other {
internal val ic = InternalClass() // 프로퍼티를 지정할 때, internal로 맞춰줘야 함.
// 근데 intellij에서 private으로 바꾸라고 권장
fun test() {
ic.i
ic.icFunc()
}
}
fun main() {
val mic = InternalClass()
mic.i
mic.icFunc()
val o = Other()
o.test()
}
Kotlin의 Internal은 프로젝트 단위의 모듈을 가리킨다. 기존 Java에서는 package라는 지시자에 의해 패키지 이름이 같은 경우 접근을 허용했으나, Kotlin에서는 패키지에 제한하지 않고 하나의 모듈 단위를 대변하는 internal을쓴다.
만약 프로젝트에 모듈이 하나일 경우, internal의 접근 범위는 프로젝트 전체가 된다.
Author And Source
이 문제에 관하여(Kotlin Study 06_1), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@parkchaebin/Kotlin-Study-061저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)