<퀴즈>
탑레벨의 숫자 데이터 10개를 가지는 immutable 리스트 2개를 생성하고,
리스트 2개를 인자로 받아 같은 인덱스의 값을 비교하여 큰 값만 새로운 리스트에 넣어 리턴하는 탑레벨 함수를 만들고 (두개의 리스트의 크기가 다르면 null 리턴) 리턴받은 리스트의 값들을 인덱스와 함께 출력한다. (idx : value)
1. 새로운 테스트 코틀린 파일을 생성한다.
2. 값 비교는 if 표현식으로 탑레벨의 max 함수를 작성한다.
3. 리턴 받는 List는 탑레벨의 lateinit 으로 작성한다. 또한 이 변수는 작성한 함수 안에서 접근하지 않고 리턴된 리스트만 받는다.
# 객체지향 프로그래밍
- PIE
1. Polymorphism(다형성)
* overloading : 같은 이름의 함수(연산자)를 다른 타입으로 같이 사용 → 파라미터만 다름
* overriding : 다른 타입의 객체를 같은 인터페이스로 같이 사용 → 메소드 재정의. 파라미터 같음
2. Inheritance(상속성) : 부모 객체로부터 코드를 상속받아 그대로 재사용
3. Encapsulation(은닉성) : 최소한으로 필요한것만 노출시키고 나머지는 숨김
* public, private 제어자 사용
# Class 선언 방법
- 다양한 형태로 클래스 선언 가능
- 멤버변수, 멤버 함수를 선언할 수 있음
- 내부 클래스 가능
- 기본 생성자, 보조 생성자 → 생성자 오버로딩 느낌?
- 인스턴스 생성시 별도의 new 필요 없음
class MyClass1 {}
class MyClass2
class MyClass3 {
val v1 = 10
var v2 = "hello"
}
class MyClass4 constructor(val v1: Int = 10, val v2: String = "none")
// constructor 키워드 생략 가능
class MyClass5(val v1: Int = 10, val v2: String = "none")
// 접근 제한자를 쓸 경우에는 constructor 키워드 생략 불가
class MyClass6 private constructor(var v1: Int = 10, val v2: String = "none")
class MyClass7(pv1: Int = 10, pv2: String = "none") {
// 아래 두 변수는 멤버변수 아님(?)
var v1 = pv1
var v2 = pv2
}
class MyClass8(v1: Int = 10, v2: String = "none") {
var v1: Int
var v2: String
init { // initialization block, 없으면 오류
this.v1 = v1
this.v2 = v2
}
}
class MyClass9 {
var v1: Int
var v2: String
constructor() : this(0) {} // 이것만 있으면 error
constructor(v1: Int, v2: String = "none") {
this.v1 = v1
this.v2 = v2
}
}
@Test
fun T02() {
var class3 = MyClass3()
println("${class3.v1}, ${class3.v2}")
// 출력 : 10, hello
var class4_1 = MyClass4(1,"aaa")
var class4_2 = MyClass4(v2="zzz")
println("${class4_1.v1}, ${class4_1.v2}")
println("${class4_2.v1}, ${class4_2.v2}")
// 출력 : 1, aaa
// 10, zzz
// var class6 = MyClass6() // 생성자가 private 이므로 인스턴스 생성 불가
var class7 = MyClass7()
println("${class7.v1}, ${class7.v2}")
// 출력 : 10, none
var class8 = MyClass8(4, "test")
println("${class8.v1}, ${class8.v2}")
// 출력 : 4, test
var class9_1 = MyClass9()
var class9_2 = MyClass9(111, v2 = "ggggg")
println("${class9_1.v1}, ${class9_1.v2}")
println("${class9_2.v1}, ${class9_2.v2}")
// 출력 : 0, none
// 111, ggggg
var class10 = MyClass10()
class10.print()
// 출력
// 0 : 1, a
// 1 : 0, none
// 2 : 0, a
}
# Class - property
- 멤버 변수는 모두 property
- 자동으로 get변수이름, set변수이름으로 getter, setter 생성
- 재정의 가능 : 검사, 로깅, 제약 등을 위해 재정의
- getter, setter에서는 field(backing field - 후원필드)로 자신 접근
* 변수 이름으로 접근할 경우 또 다시 getter, setter가 호출되며 무한 반복되므로 스택 오버플로우 발생
- lateinit 사용 가능
- Nullable ? 사용 가능
class MyClass12 {
var count = 100
get() {
println("myclass12 count get")
return field
}
set(value: Int) {
println("myclass12 count set")
field = value
}
val rocount = 1
}
@Test
fun T02() {
val mc12 = MyClass12()
println("${mc12.count}")
mc12.count = 111
println("${mc12.count}")
}
- 출력
myclass12 count get
100
myclass12 count set
myclass12 count get
111
# Class - 가시성 제한자
- private : 정의된 곳에서만 사용 가능
- protected : 상속시 서브클래스까지만 사용 가능
- public : 어디서나 사용 가능 (default)
- internal : 같은 모듈에서 사용 가능
# Class - Extentions(확장함수)
- 기존 클래스에 함수를 추가할 수 있음. (이미 정의된 클래스에 내가 원하는 기능을 추가하는 것)
- 기본형(Int 등)도 클래스이기 때문에 확장 함수 작성 가능.
- 확장 property도 확장 함수처럼 추가 가능
* 확장 property는 필드 없음 : 상태 저장불가. getter, setter만 가지고 기존의 데이터를 조작하는 것만 가능.
* 단순히 확장함수를 좀 짧게 만드는 효과
* 필드 없음 : backing field 없음. 기본 getter/setter 없음. 직접 구현 필요.
- 외부에서 사용할때 확장함수까지 import 해야 사용 가능
ex) import Int (X)
import Int.addex (O)
- 상속 안됨 : override 불가
- Receiver type: 확장이 정의될 클래스 ex) String, Int
- Receiver object: 그 클래스의 인스턴스 객체 ex) this
- 내부적으로 확장함수는 receiver object를 첫번째 인자로 받는 정적 메소드
fun Int.addex(a: Int) = this + a
println("${1.addex(2)}")
- 자바 디컴파일
public final class T02ClassKt {
public static final int addex(int $receiver, int a) {
return $receiver + a;
}
}
# nested, inner class
- Class 내부의 class
1. nested (중첩 클래스)
- 아무 키워드 없이 사용되는 내부 클래스
- 내부 클래스는 java의 static 중첩 클래스와 동일
- 외부 클래스 멤버 참조 불가
- 외부 인스턴스 없이 인스턴스 생성 가능
2. inner (내부 클래스)
- inenr 키워드 사용
- 외부 클래스 멤버 참조 가능
* this@외부클래스 이름으로 접근
- 외부 인스턴스 없이 인스턴스 생성 불가능
- 둘의 차이점 : 이너 클래스는 특별한 클래스. 자신을 포함한 클래스(상위)가 인스턴스화 되어야만 자신도 인스턴스화 가능. 상위 멤버들이 존재해야 접근 가능하므로
class Outter {
var v1 = 0;
var s1 = "outter"
var o1 = "outter o1"
fun print() = println("$s1, $v1, $o1")
inner class Inner {
var v1 = 1;
var s1 = "Inner"
fun print() {
this@Outter.print();
println("$s1, $v1, $o1")
}
}
}
var o = Outter().Inner();
o.print()
- 출력
outter, 0, outter o1
Inner, 1, outter o1
# 연산자 오버로딩, infix
- Operator 가 함수로 매핑됨.
- 매핑된 멤버 함수앞에 operator 키워드 사용
- 매핑된 함수 그대로도 사용 가능
- operator fun invoke
* () 함수호출 연산자로 사용 가능
var a = Account(10)
a() -> invoke
- 멤버 함수앞에 infix 키워드를 사용해서 중위 표현식으로 사용할 수 있음.
class Account(val age: Int) {
operator fun plus(account: Account): Account {
return Account(age + account.age)
}
operator fun minus(account: Account): Account {
return Account(age - account.age)
}
override fun toString(): String {
return "$age years old"
}
}
@Test
fun T02() {
println(Account(10) + Account(20))
println(Account(20) - Account(10))
println(Account(10).plus(Account(20)))
println(Account(23))
}
- 출력
30 years old
10 years old
30 years old
23 years old
# 상속
class 클래스이름 : 상속클래스이름(), 인터페이스1, 인터페이스2 {…}
- 인터페이스는 다중상속이 가능하지만, 클래스는 불가능
- Kotlin에서 정의한 클래스는 기본적으로 final(default)
* 기본적으로 상속불가능
* superclass 에 open 키워드를 붙여야 상속가능
* abstract, interface는 기본적으로 open
- 기본적으로 Any 클래스를 상속
- 부모 클래스에 기본 생성자가 있다면, 자식 클래스에서 위임
- super 키워드를 통해 부모 멤버 접근 가능
* 부모가 여러 개일 경우 super@부모이름
- open 된 메소드 overriding 할때 override 키워드 사용
* Override 된 메소드를 또다시 상속 받을때 overriding 하지 못하게 하려면 final
- 프로퍼티도 똑같이 overriding 가능
* open된 프로퍼티에 대해서만 가능
* open / override 해주어야함
# interface와 abstract class
1. interface
interface 인터페이스이름{…}
- class 접근제한 default : open
- 메소드 접근제한 default : open
- 껍데기만 존재하기 때문에 직접 인스턴스 생성 불가
- 서브클래스는 모든 추상함수(abstract)를 반드시 overriding 해야함
- 일반 메소드 가질 수 있음
- 추상 클래스와 다른 점은 인터페이스는 state 저장 불가
* 프로퍼티는 가질 수 있음. 프로퍼티에 값 할당은 불가능하지만, getter/setter로 다른 데이터에서 가능(?)
* 자식 클래스에서 override 하여 값 할당 가능
2. abstract(추상클래스)
abstract class 클래스이름{…}
- 특정 메소드만 추상화시킨것. 인터페이스는 껍데기만 있는 반면, 추상 클래스는 어느정도 클래스 역할을 함
- 추상 메소드에 abstract 지정자 사용
- 직접 인스턴스 생성 불가
- 서브클래스는 추상함수를 반드시 overriding 해야함
- 일반 메소드 가질 수 있음
- 프로퍼티 가질 수 있음. 값 저장 가능
- 메소드 default final
- 클래스 default open
- 인터페이스와 다른 점은 state 저장 가능, 인터페이스는 모든 멤버가 open 이지만 추상클래스는 메소드는 final이다.
open class Parent(var a: Int) {
fun funB() {
println("ParentA funB()")
}
open fun funA() {
println("Parent funA()")
}
}
interface IParent {
fun funA() { // abstract
println("IParent funIA()")
}
}
data class Example(var age: Int) : Parent(age), IParent {
constructor(name: String, age: Int) : this(age) {
println("$name $age")
}
// 여기서 funB 를 정의하려고 하면 override 키워드를 붙이라는 오류가 난다
// 하지만 부모클래스에서 funB()가 open 되어있지 않기 때문에 오버라이드가 불가능하다
override fun funA() {
println("Child funA()")
super<IParent>.funA()
super<Parent>.funA()
funB()
}
}
interface MyInterface {
val prop: Int // abstract
val propertyWithImplementation: String
get() = "foo" // get override
// getter/setter 오버라이드 하려면 해당 변수 밑에 해야하는걸 방금 알았음
fun foo(){
print(prop)
}
}
// 변수 오버라이딩
// 아래와 같이 재정의 할 경우에만 인터페이스의 프로퍼티에 값 할당 가능
data class Child (override val prop: Int = 23): MyInterface
@Test
fun T02() {
var ex = Example("delay", 1)
println("$ex")
ex.funA()
println("--------------------------")
// 다형성을 보여주는 예
var ex1: Parent = Example(23)
// 오버라이드된 Example(자식)의 funA()가 호출된다.
// 하나의 슈퍼클래스로 여러 자식의 메소드를 호출할 수 있다. (다운캐스팅 이었던가?)
ex1.funA()
println("==========================")
var ch = Child()
println("$ch : ${ch.propertyWithImplementation}")
ch.foo()
}
- 출력
delay 1
Example(age=1)
Child funA()
IParent funIA()
Parent funA()
ParentA funB()
--------------------------
Child funA()
IParent funIA()
Parent funA()
ParentA funB()
==========================
Child(prop=23) : foo
23
# object 키워드
1. 객체 선언
object ObjectName { … }
- class 대신 object 로 작성
- 따로 인스턴스화 시킬 필요 없음 : 최초로 생성되는 시점에 생성되고, 선언과 동시에 인스턴스화됨
- 생성자 가질 수 없음.
- 상속받거나 인터페이스 구현 가능
- 클래스 내부에서도 사용 가능
* 외부 클래스가 여러 개 인스턴스화 되어도 하나만 생성됨
* Outer.InnerObject.xxx 로 사용
* 자바에서 사용할때는 Outer.InnerObject.INSTANCE.xxxx 로 사용
+ 클래스 생성시 (). 로 인스턴스화(new) 했는데
이럴 필요 없이 바로 접근 가능
2. companion(동반객체)
compainon object ObjectName {…}
- 코틀린에서 static method 역할을 하는 내부 객체
- Outer 객체의 생성자를 private 으로 하고 팩토리 함수를 만들때 유용
* Outer 의 private 에 접근할 수 있는 static 객체
- 코틀린에서는 Outer.InnerObjectMethod 로 바로 사용 가능
- 자바에서는 Outer.Companion.InnerObjectMethod 로 사용
* @JvmStatic 어노테이션을 사용하여 코틀린처럼 사용 가능
- 생성자 private -> 객체 생성 불가
* 자바 : public static 멤버 함수를 만들어 생성자 접근
* 코틀린 : companion 객체를 이용, static 처럼 접근할 수 있기 때문에 생성자를 접근하여 FactoryMethod로 이용
3. 객체표현식
object : superclass() { … }
- 익명객체를 생성하는 방법
- 객체선언과 비슷하나 이름이 없음.
- 클로저 생성
* 외부에 있는 변수 사용 가능. 뿐만 아니라 호출된 환경(?)을 가져올 수 있음 ex) 안드로이드의 경우, 익명클래스 내부에서 그 바깥에 있는 변수들에 접근할 수 있는 것
- 싱글톤 아님 (매번 인스턴스 생성)
- 리스너 객체 생성에 이용 가능
data class Person3 private constructor(val name: String, val age: Int, var litener: TListener? = null)
{
fun doFire() = litener?.onEvent()
companion object {
fun makePerson(name: String, age: Int): Person3
{
var tmp = age
return Person3(name, age, object: TListener {
override fun onEvent() {
println("evt2 : $tmp")
}
})
}
}
}
# data 키워드, component+n
data class name
- 기존 java 에서 Lombok 외부 라이브러리 사용한 기능 기본으로 제공
- 변수 선언 순서대로 component 번호 부여
- 자동으로 생성되는 메소드
* constructor
* getter/setter
* hashcode/equals
* toString
* componentN() functions
* copy() function
data class User(val id: Long, val name: String)
@Test
fun T02() {
var u = User(0, "name")
println("$u")
println("${u.id}, ${u.name}")
println("${u.component1()}, ${u.component2()}")
val (id, name) = u
println("id : $id name : $name")
}
- 출력
User(id=0, name=name)
0, name
0, name
id : 0 name : name
# 위임 (Delegate)
class Derived(baseImpl: Base) : Base by baseImpl
- 코틀린은 기본적으로 class의 상속을 제한
* final class 가 기본값
* 상속을 하려면 open class 해야함
- 특정 객체의 기능을 재사용하기 위해 상속 이외에 위임을 할 수 있음
- 특정 인터페이스의 기능을 다른 객체에 위임함.
- 그냥 클래스 안에서 다른 객체 선언해서 갖다쓰는거라고 이해함.
interface Vehicle {
fun go(): String
}
class CarImpl(val where: String): Vehicle {
override fun go() = "is going to $where"
}
class AirplaneImpl(val where: String): Vehicle {
override fun go() = "is flying to $where"
}
class CarOrAirplane(val model: String, impl: Vehicle): Vehicle by impl {
fun tellMeYourTrip() {
println("$model ${go()}")
}
}
@Test
fun T02() {
val myAirbus330 = CarOrAirplane("Lamborghini", CarImpl("Seoul"))
val myBoeing337 = CarOrAirplane("Boeing 337", AirplaneImpl("Seoul"))
myAirbus330.tellMeYourTrip()
myBoeing337.tellMeYourTrip()
}
- 출력
Lamborghini is going to Seoul
Boeing 337 is flying to Seoul
# enum
enum class ClassName
- enumType 의 상수 객체 항목들.
- 값을 가질 수 있고, 객체로 선언 될 수 있다.
- when 표현식을 사용할때 모든 열거형 사용했다면 else 없어도 사용 가능
- enumType.value.ordinal 로 순서
- enumType.value.name 으로 이름 사용
- 멤버 함수 가능
* 멤버 함수는 항목들이 다 나온 후에 나와야 함.
* 마지막 항목에는 세미콜론 필요.
(따로 공부필요한 부분.... 예제 놓쳤음)
# sealed
sealed class ClassName
- 자신의 sub class 의 종류를 제한
* Sealed class 는 하위 클래스들은 sealed class 와 같은 파일에 선언되어야함
- when 표현식을 사용할때 모든 경우를 사용했다면 else 없어도 사용 가능
- sealed 는 추상 클래스 : 추상멤버 가능
- 비공개 생성자 불가능 (private 생성자인가?)
# 출처 : 모베란 백지훈 대표이사님
'Programming > Kotlin' 카테고리의 다른 글
[Kotlin]코틀린을 이용한 안드로이드 프로그래밍 실습 04 (0) | 2018.07.05 |
---|---|
[Kotlin]코틀린을 이용한 안드로이드 프로그래밍 실습 03 (0) | 2018.07.04 |
[Kotlin] 코틀린을 이용한 안드로이드 프로그래밍 실습 01-2 (0) | 2018.07.02 |
[Kotlin] 코틀린을 이용한 안드로이드 프로그래밍 실습 01-1 (0) | 2018.07.02 |