GDSC HUFS 3기/Android with Kotlin Team 1

[1팀] Section 7: Kotlin - Object Oriented Programming(1)

dev-yen 2021. 10. 7. 03:28

이 글은 udemy 강의를 참고하여 작성하였습니다.

작성자 : 이재성

개발환경은 Windows, Android Studio입니다.

 

 

1. 함수 (Functions)


Java와 Kotlin에서의 함수는 크게 다르지 않다. 아래의 예시를 통해 Java와 Kotlin에서의 함수 정의 방법을 확인한다.

// Java
public String getName() {
		return "name";
}

// Kotlin
fun getName(): String {
		return "name"
}

 

함수 정의에 있어서 Java와 다른 점은 반환할 데이터 타입을 함수 명 뒤에 붙인다는 점이다. 위의 예시에서 선언한 함수는 두 번 더 축약할 수 있다.

fun getName(): String = "name"

fun getName() = "name"

 

Kotlin에서 함수의 Default 반환 데이터 타입은 Unit이다. 즉, 아무것도 return하지 않는 Void함수를 의미한다.

또한, 함수의 결과 값 자체를 String Template을 이용하여 출력할 수 있다. 아래의 예시를 통해 확인할 수 있다.

fun getSum(a: Int, b: Int): Int = a + b

println("Result is ${getSum(1, 3)}")

// Result is 4

 

2. 객체 지향 프로그래밍 (Object-Oriented Programming)


객체 지향 프로그래밍, OOP는 프로그래밍에 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체(Object)를 생성하고, 객체들 간의 상호작용을 통해 구성하는 프로그래밍 방법이다. 쉽게 말해, 요구사항을 관리하기 쉬운 하나의 덩어리 즉, 객체로 나누는 것이다. 각각의 덩어리는 독립적이며 재사용이 가능하다.

객체는 변수, 함수로 구성된 대상이다. 여기서 변수는 Property, Attribute를 통칭하며, 함수는 Methods 또는 Behaviour로 부른다.

위 그림은 자동차 모델을 기반으로 여러가지 객체를 생성하는 것을 보인다. 위에 설명한 것 처럼, Car Class의 Property는 모델, 가격, 색상, 연비 등으로 이루어질 것이고, Method는 가속, 감속, 정지 등으로 이루어질 것이다. 하단에 있는 Red, Blue, Green Car는 Car Class를 기반으로 만들어진 각각의 자동차 객체이며 이를 인스턴스 (Instance)라고 한다.

 

3. OOP의 특징


OOP는 다음과 같은 세가지 특징을 갖는다.

  1. 캡슐화 (Encapsulation)
    생성한 객체의 Property와 Method를 은닉한다. 이렇게 캡슐화된 Property와 Method는 외부에서 접근할 수 없다. 또한, 외부의 잘못된 사용으로 인해 객체가 손상되지 않도록 하는데에 의의를 갖는다.

  2. 다형성 (Polymorphism)
    어떤 Class를 상속하다 보면 자식 Class에서 부모 Class와 같은 이름의 Property나 Method를 사용하는 일이 있다. 이때, 자식 Class에서 이름은 같지만 사용하는 Parameter가 다르거나 아예 다른 동작을 수행하는 Method를 정의할 필요가 있다. 이와 같은 행위를 다형성이라고 한다.

  3. 상속 (Inheritance)
    Class의 기능을 확장하고자 할 때, 현재 Class의 기능을 포함하여 새로운 기능이 추가된 새로운 Class를 정의하는 방법이다. 이렇게 상속을 하게 되면 두 클래스는 부모-자식의 관계를 갖게 된다. 자식 Class는 부모 Class의 속성을 물려받게 되며 이 경우, 중복을 방지할 수 있다. 또한, 부모 Class의 수정은 자식 Class에도 영향을 미치게 된다. 따라서 수정에 대한 여파가 적다고 볼 수 있다.

 

4. Class 정의


Kotlin에서 Class는 class 키워드를 사용하여 정의한다.

class Soldier { 
		/*
		 멤버 변수 (클래스 변수, 인스턴스 변수)
		 멤버 함수
		*/
}
class Message { // 구현 }
class PracticleSystem { // 구현 }

class MainActivity: AppCompatActivity() {
		
		override fun onCreate(...) {
				
				// 인스턴스화
				val soldier1 = Soldier()
				val textMessage = Message()
				val system1 = PracticleSystem()
		}
}

위의 예시에서는 세 가지 Class를 정의하고, Main이 되는 Class에서 각 Class의 인스턴스를 생성한다. Class에는 Class내에 정의되는 멤버 변수, 멤버 함수를 정의한다.

 

아래 예시는 위의 예시에서 멤버 변수와 멤버 함수를 정의한 코드이다.

class Soldier {  
		val name = "John"        // 클래스 변수
		val rank = "Private"
		var missing: Boolean     // 인스턴스 변수

		fun getStatus() {        // 멤버 함수
		    var status = "$rank"
				if (missing) {
						status = "$status SOLDIER IS MISSING"
				} else {
						status = "$status, READY FOR DUTY"
				}
				Log.i("STATUS", status)
		}
}

class MainActivity: AppCompatActivity() {
		
		override fun onCreate(...) {
				
				val soldier1 = Soldier()
				println(soldier1.name)

				soldier1.getStatus()
		}
}

멤버 변수 중 클래스 변수는 Class내에 명시적으로 선언된 변수이다. 인스턴스 변수는 Class내에 데이터 타입만 선언하고 인스턴스를 통해 호출하면서 값을 유동적으로 바꿀 수 있는 변수이다.

MainActivity Class에서 Soldier Class의 인스턴스를 생성하고 이 인스턴스를 이용하여 Soldier Class의 멤버 변수와 멤버 함수를 호출한다.

 

5. Getter & Setter

Java와 비교했을 때 Kotlin의 장점 중 하나는 접근자 사용의 편리함이다. 아래의 예시를 통해 확인한다.

class Person(val name: String)

Java로 작성할 때 여러 줄로 작성한 코드를 Kotlin에서는 단 한 줄의 코드로 대체할 수 있다. Kotlin에서는 위와 같이 변수를 만들어주기만 해도 접근자 즉, gettersetter를 동시에 내부적으로 생성한다.

Kotlin에서는 기본적으로 public한 스코프를 갖는다. Java와는 달리 public을 따로 쓸 필요가 없다. 위의 예시에서 name은 private로 생성 된다.

 

val로 선언된 name은 Read-Only의 Property이고, getter만 가질 수 있다. var로 선언된 Property의 경우 gettersetter 모두 갖는다. 아래의 예시를 통해 확인한다.

class Soldier {  
		var bullets = 100
				get() {
						Log.i("GETTER IS USED", "VALUE = $bullets")
						return field        
				}
				set(value) {
						field = if (value < 0) 0 else value
						Log.i("SETTER IS USED", "NEW VALUE = $field")
				}
}

class MainActivity: AppCompatActivity() {
		
		override fun onCreate(...) {
				
				val soldier1 = Soldier()    // Soldier 클래스 인스턴스 선언
				
				Log.i("BULLETS", "${soldier1.bullets}")    // Getter 실행
				
				soldier1.bullets--
				Log.i("BULLETS", "${soldier1.bullets}")    // Setter 실행	
		}
}

// Getter 실행
// I/GETTER IS USED: VALUE = 100
// I/BULLETS: 100

// Setter 실행
// I/GETTER IS USED: VALUE = 100
// I/SETTER IS USED: NEW VALUE = 99
// I/GETTER IS USED: VALUE = 99
// I/BULLETS: 99

MainActivity Class에서 생성된 soldier1 인스턴스를 이용하여 Soldier Class의 Property에 접근할 수 있다. 주석 처리된 결과 출력문을 통해 확인할 수 있다.

 

6. Public, Private, Protected, Internal


Kotlin에서는 Class 내부에 멤버들을 선언하고 외부에서 참조하게 할 수 있다. 그러나, 무분별한 참조는 캡슐화가 깨질 수 도 있다. 따라서, 각각의 멤버에 가시성 변경자를 설정할 필요가 있다.

public은 모든 곳에서 접근할 수 있게 한다. 이렇게 선언된 멤버는 캡슐화되지 않고 공개된다. private은 동일 파일 내에서만 사용 가능하며 인스턴스화 할 수 있다. 즉, 같은 Class 내부에서만 접근이 가능하다. protected은 Class의 내부 혹은 상속받는 객체에서만 접근 가능하다. internal은 같은 모듈 안에서만 접근할 수 있다.

class SatelliteController {
		var gpsCoordinates = "55.2934592"    // SatelliteController.kt 내에서는 변경 가능
		private set                          // 외부에서 gpsCoordinates에 접근 가능하지만 변경은 불가

		private fun dropOutOfTheSky() {
		
		}
}

위의 예시에서 gpsCoordinates 멤버 변수는 기본적으로 public으로 선언되어 있다. 하지만, private set을 지정하게 되면 var라고 선언되어 있더라도 외부에서 gpsCoordinates에 접근 가능하지만 변경은 할 수 없게 된다. 즉, SatelliteController.kt 파일 내에서만 변경이 가능한 멤버이다.