Posts 이것이 안드로이드다 with 코틀린 #1 코틀린 기본문법: 7. 클래스와 설계
Post
Cancel

이것이 안드로이드다 with 코틀린 #1 코틀린 기본문법: 7. 클래스와 설계

Chapter 03. 코틀린 기본문법

7. 클래스와 설계

그룹화할 수 있는 함수와 변수를 한 군데 모아놓고 사용하기 쉽게 이름을 붙여 놓은 것을 클래스라고 한다.

클래스의 기본구조

1
2
3
4
5
6
class 클래스명 {
  var 변수
  fun 함수() {
    // 코드
  }
}

클래스의 생성

  • 코틀린은 객체를 사용하기 위해 두 가지 형태의 생성자(함수)를 제공한다.

  • 클래스를 사용한다는 것은 곧 클래스라는 그룹으로 묶여 있는 코드를 실행하는 것이기 때문에 함수 형태로 제공되는 생성자를 호출해야지만 클래스가 실행되는 것이다.

  • 생성자를 사용하지 않을 경우에는 생성 시 자동으로 기본 생성자가 호출된다. 기본 생성자는 파라미터가 아무것도 없는 빈 코드 블록이다.

    1
    2
    3
    4
    5
    
    class kotlin {
      init {
        // 생성자가 없으면 아무것도 없는 init 블록이 실행되는 것과 같다. 
      }
    }
    

프라이머리 생성자

클래스의 헤더처럼 사용할 수 있으며, constructor 키워드를 사용해서 정의하는 데 조건에 따라 생략할 수 있다. 프라이머리 생성자도 결국은 함수이기 때문에 파라미터를 사용할 수 있다.

1
2
3
class KotlinOne constructor(value: String) {
  // 코드
}

생성자에 접근 제한자나 특정 옵션이 없다면 constructor 키워드를 생략할 수 있다.

1
2
3
class KotlinOne (value: String) {
  // 코드
}

프라이머리 생성자는 마치 헤더처럼 class 키워드와 같은 위치에 작성되기 때문에 생성자 옆에 코드 블록을 작성할 수 없지만, 이는 init 블록으로 대체할 수 있다. 클래스의 생성자가 호출되면 init 블록의 코드가 실행되고, init 블록에서는 생성자를 통해 넘어온 파라미터에 접근할 수 있다.

1
2
3
4
5
class KotlinTwo(value: String) {
  init {
    Log.d("class", "생성자로부터 전달받은 값은 ${value}입니다.")
  }
}

세컨더리 생성자

1
2
3
4
5
class KotlinTwo {
  constructor (value: String) {
    Log.d("class", "생성자로부터 전달받은 값은 ${value} 입니다.")
  }
}

세컨더리(Secondary) 생성자는 constructor 키워드를 마치 함수처럼 사용해서 작성할 수 있다.

클래스의 사용

클래스의 이름에 괄호를 붙여서 클래스의 생성자를 호출한다. constructor 키워드를 직접 호출하지는 않는다.

아무런 파라미터 없이 클래스 명에 괄호를 붙여서 호출하면 init 블록이 있는 생성자가 호출되면서 블록 안의 코드가 자동으로 실행된다. 세컨더리 생성자의 경우 constructor 블록 안의 코드가 실행된다.

클래스 안에 정의된 함수와 변수 사용하기

생성자를 통해 변수에 저장된 클래스의 인스턴스는 내부에 정의된 변수와 함수를 도트 연산자로 접근할 수 있다.

프로퍼티와 메서드

  • 클래스의 변수 > 멤버 변수 > 프로터피(Property)
  • 클래스의 함수 > 멤버 함수 > 메서드(Method)

클래스 안에 정의된 변수는 프로퍼티라고 하지만 함수 안에 정의된 변수는 프로퍼티라고 하지 않고 그냥 변수(지역 변수)라고 한다.

클래스를 인스턴스화 하지 않고 사용하기: companion object

companion object 코드 블록을 사용하면 클래스를 생성자로 인스턴스화하지 않아도 블록 안의 프로퍼티와 메서드를 호출해서 사용할 수 있다.

1
2
3
4
5
6
7
8
class KotlinFour {
  companion object {
    var one: String = "None"
    fun printOne() {
      Log.d("class", "one에 입력된 값은 ${one}입니다.")
    }
  }
}
1
2
KotlinFour.one = "새로운 값"
KotlinFour.printOne()

Log 클래스의 메서드 d(), e() 모두 companion object 코드 블록 안에 만들어져 있기 때문에 생성자 없이 바로 호출해서 사용할 수 있었다.

데이터 클래스

코틀린은 간단한 값의 저장 용도로 데이터 클래스(data class)를 제공한다. data 예약어를 class 앞에 붙여서 만들고 클래스명 다음에 생성자처럼 파라미터를 정의할 수 있다. 생성자와 다르게 파라미터 앞에 var 또는 val을 명시해서 변수인지 상수인지 구분해야 한다.

1
data class 클래스명 (val 파라미터1: 타입, var 파라미터2: 타입)

데이터 클래스의 정의와 생성

  • 파라미터 앞의 var(또는 val)은 생략할 수 없다.
  • 일반 클래스에서 toString() 메서드를 호출하면 인스턴스의 주소값을 반환하지만, 데이터 클래스는 값을 반환하기 때문에 실제 값을 모니터링할 때 좋다.
1
2
3
4
data class DataUser (var name: String, var age: Int)
var dataUser = DataUSer("Michael", 21)
Log.d("DataClass", "DataUser는 ${dataUser.toString()}")
// DataUser는 DataUser(name=Micael, age=21)

copy() 메서드 지원

copy() 메서드로 간단하게 값을 복사할 수 있다.

1
var newData = dataUser.copy();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 파라미터가 없는 클래스
class KotlinOne() {
  init {
    Log.d("class", "Kotlin 클래스 생성됨")
  }
}

// 파라미터가 있는 세컨더리 생성자
class KotlinTwo() {
  constructor (value: String) {
    Log.d("class", "KotlinTwo: 파라미터 값은 ${value}입니다.")
  }
}

// 프로퍼티와 메서드가 있는 클래스
class KotlinThree {
  var one: String = "None"
  fun printOne() {
    Log.d("class", "KotlinThree: one에 입력된값은 ${one}입니다.")
  }
}

// 스태틱 멤버를 갖는 클래스
class KotlinFour {
  companion object {
    var one: String = "None"
    fun printOne() {
      Log.d("class", "KotlinFour: one에 입력된 값은 $one입니다.")
    }
  }
}

data class DataUser(var name: String, var age: Int)

클래스의 상속과 확장

  • 상속을 사용하면 메서드와 프로퍼티를 마치 내 클래스의 일부처럼 사용할 수 있다.
  • 안드로이드에는 Activity라는 클래스가 미리 만들어져 있고, Activity 클래스 내부에는 글자를 쓰는 기능, 그림을 그리는 기능, 화면에 새로운 창을 보여주는 기능들이 미리 정의되어 있다. 상속이 있기 때문에 Activity 클래스를 상속받아 약간의 코드만 추가하면 간단한 앱 하나를 만들 수 있다.
1
2
3
4
5
6
class Activity {
  fun drawText()
  fun draw()
  fun showWindow()
  ...
}
1
2
3
4
5
class MainActivity : Activity() {
  fun onCreate() {
    draw("새 그림")
  }
}

클래스의 상속

  • 상속 대상이 되는 부모 클래스는 open 키워드로 만들어야만 자식 클래스에서 사용할 수 있다. open 키워드로 열려 있지 않으면 상속할 수 없다.
  • 상속을 받을 자식 클래스에서는 콜론을 이용해서 상속할 부모 클래스를 지정할 수 있다.
  • 클래스 상속은 부모의 생성자를 호출해서 생성된 인스턴스를 자식이 갖는 과정이기 때문에 부모 클래스명 다음에 괄호를 입력해서 생성자를 호출한다.
1
2
3
4
5
6
open class 상속될 부모 클래스 {
  // 코드
}
class 자식 클래스 (value: String) : 부모 클래스(value) {
  // 코드
}

생성자 파라미터가 있는 클래스의 상속

상속될 부모 클래스의 생성자에 파라미터가 있다면 자식 클래스의 생성자를 통해 값을 전달할 수 있다.

1
2
3
4
5
6
7
8
open class 부모 클래스 (value: String) {
  // 코드
}

// 파라미터를 전달하는 개념이다. 
class 자식 클래스 (value: String) : 부모 클래스 (value) {
  // 코드
}

부모 클래스에 세컨더리 생성자가 있다면, 역시 자식 클래스의 세컨더리 생성자에서 super 키워드로 부모 클래스에 전달할 수 있다.

부모 클래스의 세컨더리 생성자를 이용하는 경우는 부모 클래스명 다음에 오는 괄호를 생략한다.

1
2
3
4
class CustomView : View(생략) {
  constructor(ctx: Context): super(ctx)
  constructor(ctx: Constext, attrs: AttributeSet): super(ctx, attrs)
}

부모 클래스의 프로퍼티와 메서드 사용하기

부모 클래스에서 정의된 프로퍼티와 메서드를 내 것처럼 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
open class Parent {
  var hello: String = "안녕하세요."
  fun sayHello() {
    Log.d("inheritance", "${hello}")
  }
}

class Child : Parent() {
  fun myHello() {
    hello = "Hello!"
    sayHello()
  }
}

프로퍼티와 메서드 재정의: 오버라이드

동일한 이름의 메서드나 프로퍼티를 사용할 필요가 있을 경우에 override 키워드를 사용해서 재정의할 수 있다.

메서드 오버라이드

상속할 메서드 앞에 open 키워드를 붙이면 오버라이드할 수 있지만, open 키워드가 없는 메서드는 오버라이드할 수 없다.

1
2
3
4
5
6
7
8
open class BaseClass {
  open fun opened() {}
  fun notOpened() {}
}
class ChildClass : BaseClass() {
  override fun opened() {}
  //override fun notOpened() {} // open 키워드가 없으므로 잘못된 사용이다. 
}

프로퍼티 오버라이드

마찬가지로 open으로 열려 있어야만 오버라이드를 할 수 있다.

1
2
3
4
5
6
open class BaseClass2 {
  open var opened: String = "I am"
}
class ChildClass2 : BaseClass2() {
  override var opened: String = "You are"
}

익스텐션

코틀린은 클래스, 메서드, 프로퍼티에 대해 익스텐션(extension)을 제공한다. 이미 만들어져 있는 클래스에 다음과 같은 형태로 메서드를 추가할 수 있다.

1
2
3
fun 클래스.확장할 함수() {
  // 코드
}

상속이 미리 만들어져 있는 클래스를 가져다 쓰는 개념이라면 익스텐션은 미리 만들어져 잇는 클래스에 메서드를 붙여넣는 개념이다.

확장을 한다고 해서 실제 클래스의 코드가 변경되는 것은 아니며 실행 시에 도트 연산자로 호출해서 사용할 수 있도록 해준다. 특별한 경우를 제외하고는 거의 메서드 확장 용도로 사용된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MainActivity : AppCompatAcivity() {
  override fun onCreate(savedInstance: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    
    testStringExtension()
  }
  
  // String 익스텐션 테스트 하기
  fun testStringExtension() {
    var original = "Hello"
    var added = " Guys~"
    // plus 함수를 사용해서 문자열을 더할 수 있따.
    Log.d("Extension", "added를 더한 값은 ${original.plus(added)}입니다.")
  }
}

fun String.plus(word: String): String {
  return this + word
}

상속과 확장

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class MainActivity : AppCompatAcivity() {
  override fun onCreate(savedInstance: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    
    // 1. 부모 클래스 직접 호출하기
    var parent = Parent()
    parent.sayHello()
    // 2. 자식 클래스 호출해서 사용하기
    child.myHello()
    child.myHello()
    
    testStringExtension()
  }
}

  // String 익스텐션 테스트 하기
  fun testStringExtension() {
    var original = "Hello"
    var added = " Guys~"
    // plus 함수를 사용해서 문자열을 더할 수 있따.
    Log.d("Extension", "added를 더한 값은 ${original.plus(added)}입니다.")
  }
}

fun String.plus(word: String): String {
  return this + word
}
This post is licensed under CC BY 4.0 by the author.

HIWORK: could not extract ResultSet 에러

모던 자바스크립트 #5.6. iterator

Loading comments from Disqus ...