하나의 클래스를 다른 클래스에 위임하도록 선언하여 위임된 클래스가 가지는 인터페이스 메소드를 참조 없이 호출할 수 있도록 생성해주는 기능이다.
interface Fruit를 구현하고 있는 class Apple이 있다면, Fruit에서 정의하고 있는 Apple의 모든 메소드를 클래스 GreenApple로 위임할 수 있다. 즉, GreenApple은 Apple이 가지는 모든 Fruit의 메소드를 가지며, 이를 클래스 위임(Class delegation)이라고 한다.
interface Fruit {
val name: String
val color: String
fun bite()
}
class Apple: Fruit {
override val name: String
get() ="사과"
override val color: String
get() ="빨간색"
override fun bite() {
print("사과 아삭~ 아삭~")
}
}
// 클래스 위임 : 사용할 기능은 그대로 사용하고 새로 정의할 것만 override로 재정의
중첩클래스의 class 키워드 앞에 inner 키워드를 붙이면 내부클래스가 된다. class OuterClass{ //내부클래스 inner class InnerClass }
이 두 클래스에 대해서 공통점과 차이점을 간단히 요약해보면 다음과 같다. 공통점 : 클래스 내부에 다른 클래스로 정의된다. 외형적 차이점 : inner 키워드를 쓰면 내부클래스, 안쓰면 중첩클래스이다. 기능적 차이점 : 중첩클래스는 OuterClass의 참조를 가지지 않지만 내부클래스는 OuterClass의 인스턴스를 참조를 가진다. 중첩클래스는 외형적으로는 OuterClass 소속인 것 같지만 사실은 연관성이 거의 없다.
class OuterClass { val outerValue = 10 val a = "Outside Nested class."
inner class Inner { private val innerValue = 20 fun callMe() = a // OutClass 의 변수 할당 가능 fun printItems() { println("inner: $innerValue, outer: $outerValue") } }
fun printItems() { val inner = Inner() // 객체 생성 inner.printItems() } }
fun main(args: Array<String>) { val outer = OuterClass() // 객체 생성 outer.printItems()
자바에서는 A 클래스 안에 B 클래스를 정의하면 B 클래스는 자동으로 내부 클래스가 되었다. 하지만 코틀린에서는 반대다. 한 클래스안에 다른 클래스를 정의하면 기본적으로는 중첩 클래스가 되고, 내부 클래스로 만들고 싶다면 inner 키워드로 클래스를 선언해야 한다. 내부 클래스는 기본적으로 외부 클래스를 참조하게 되지만 중첩 클래스는 그렇지 않다.
class Outer { val a = "Outside Nested class."
class Nested { // Outer Class 변수에 접근할 수 없다. val b = "Inside Nested class." fun callMe() = "Function call from inside Nested class." } }
fun main(args: Array<String>) { // accessing member of Nested class println(Outer.Nested().b) // 자바의 static 중첩 클래스와 동일하다.
// creating object of Nested class val nested = Outer.Nested() println(nested.callMe()) }
Java에서는 데이터르 필드에 저장하며, 멤버 필드의 가시성은 보통 비공개(private)이다.
클래스는 자신을 사용하는 클라이언트가 그 데이터에 접근하는 통로로 쓸 수 있는 접근자 메소드를 제공한다.
보통은 필드를 읽기 위한 getter를 제공하고 필드를 변경 허용해야 할 경우 setter를 추가 제공할 수 있다.
Java에서는 필드와 접근자를 묶어 property 라구 부르며, 프로퍼티를 활용하는 프레임워크가 많다.
코틀린은 프로퍼티를 언어 기본 기능으로 제공한다.
class Person(val name: String, val age: Int)
코틀린에서는 Java 클래스의 getter를 val 프로퍼티처럼 사용할 수 있고, getter/setter 쌍이 있는 경우에는 var 프로퍼티처럼 사용할 수 있다.
코틀린에서는 하나의 파일에 하나 또는 여러개의 클래스를 정의할 수 있다.
하지만 유지보수의 용이성을 고려하여 가급적 하나의 클래스를 하나의 파일에 정의하는 것이 좋다.
가시성 제한자(visibility modifier)
public
함수나 속성이 클래스 외부에서 사용될 수 없다.
코틀린의 기본 가시성은 public 이므로 가시성 제한자를 생략해도 된다.
private
함수나 속성이 정의된 클래스 내부에서만 사용될 수 있다.
protected
함수나 속성이 정의된 클래스 내부 또는 클래스의 서브 클래스에서만 사용될 수 있다.
internal
함수나 속성이 정의된 클래스가 포함된 모듈에서 사용될 수 있다.
코틀린에서는 자바에 없는 internal 가시성을 지원한다. 이것은 같은 모듈에 있는 클래스, 함수, 속성까리 상호 사용할 수 있다는 것을 뜻한다.
Java는 기본적으로 패키지 가시성을 지원한다. 즉, 가시성 제한자가 없는 메서드, 필드, 클래스는 같은 package에 있는 클래스에서만 사용 가능하다는 뜻이다.
코틀린에서는 package 가시성이 없다. 같은 package에 있는 클래스, 함수, 속성 등은 기본적으로 상호 사용할 수 있어서 굳이 별도의 가시성을 가질 필요가 없기 때문이다.
서로 다른 패키지에 있는 같은 이름의 클래스나 함수 등을 함께 사용할 경우에는 as 키워드로 별칭을 지정하여 이름 충돌이 생기지 않게 할 수 있다.
클래스 속성에서 var 는 변경 가능한 속성을, val 은 읽기 전용 속성을 지정하는 데 사용된다.
코틀린 생성자
코틀린은 자바, C++과는 달리 클래스 선언 부분에서 이름에 괄호를 붙여 생성자를 만들 수 있다.
이를 주 생성자(primary constructor)라고 한다.
코틀린 몸체에 보조 생성자가(secondary constructor)를 추가할 수 있다. 보조 생성자는 매개변수를 다르게 하여 여러 개를 정의할 수 있다.
// (name: String, surname: String) default constructor signature class Person(_name: String, surname: String) { // 맨 앞에 밑줄(_)이 있는 변수는 임시 변수를 나타낸다. // 매개변수를 포함해서 임시 변수는 한번 이상 참조될 필요가 없는 변수이며, // 1회용이라는 것을 나타내기 위해 이름 앞에 밑줄을 붙인다.
val name: String
// init block , represents the body of default constructor init { //주생성자와 함께 초기화 블럭 // init 초기화 블록은 여러개 가능하다. name = _name Log.d("primary", name) }
// secondary constructor // this(name,"") call to default constructor // constructor 로부터 생성된 생성자는 '기본 생성자'를 '상속' 받아야 한다. constructor(name : String):this(name,""){ Log.d("secondary", "Hello") } }
class Person constructor(name: String) { /*...*/ }
primary constructor가 어노테이션이나 접근 제한자(public, private 등)을 갖고 있지 않다면 constructor 키워드를 생략할 수 있다. class Person(name: String) { /*...*/ }
자바의 경우 생성자로부터 받아온 변수를 해당 클래스에서 사용할 때 생성자 내에서 [this.클래스변수 = 매개변수] 같은 형식으로 받아올 수 있다. 하지만 코틀린은 생성자 영역이 없어서 이렇게 할 수가 없다.
코틀린에서는 기본과 보조 생성자에 추가하여 클래스 초기화 블럭도 정의할 수 있다.
초기화 블럭은 init 키워드로 정의한다. init 초기화 블록은 여러개 가능하다.
this.name = name
만일 속성을 초기화하는 코드가 복잡하다면 초기화 코드를 함수나 초기화 블럭에 넣은 것을 고려하자.
코틀린의 클래스는 secondary constructor도 가질 수 있다. 이때 사용되는 키워드는 constructor이고, primary constructor와 다르게 생략할 수 없다. class Person { var children: MutableList<Person> = mutableListOf<Person>(); constructor(parent: Person) { parent.children.add(this) } }
객체 지향 언어에서는 객체를 생성하기 위해서는 먼저 클래스를 선언하고, 해당 클래스로부터 객체를 생성한다.
하지만, 코틀린에서는 이런 과정을 거치지 않고도 객체를 생성할 수 있다.
object 키워드를 사용하면, 클래스가 아니라 객체 그 자체, 인스턴스가 된다.
코틀린에는 static keyword가 없고 object 키워드를 통해 - 싱글턴을 정의하는 방법 - 동반객체 companion object를 이용한 팩토리 메서드 구현 - 무명클래스(익명 클래스)의 선언 를 한다.
예제1
fun main(){ // Person은 그 자체로 하나의 객체이기 때문에, 바로 .을 찍어 프로퍼티나 메서드에 접근할 수 있다. Person.name = "홍길동" Person.age = 30 println("이름 : ${Person.name}, 나이 : ${Person.age}") }
// object 는 subclass 의 명시적인 선언 없이 객체 생성 object Person { var name: String = "" var age: Int = 0 }
예제2
- object 키워드는 어떤 객체가 반드시 하나만 존재해야 할 때, 즉 싱글톤 패턴에 활용할 수 있다.
// 예제 2.1
fun main(args: Array<String>) { Singleton.printVarName()
println("\nSingleton variableName Change") Singleton.variableName = "강감찬" var a = A() }
object Singleton{ init { println("Singleton class invoked.") } var variableName = "홍길동" fun printVarName(){ println("변수명 : $variableName ") } }
class A { init { println("Class init method. Singleton variableName property : ${Singleton.variableName}") Singleton.printVarName() } }
// 예제 2.2 fun main(args: Array<String>) { var a = A() a.printVarName()
println() Singleton.printVarName() }
open class A { open fun printVarName() { println("I am in class printVarName") }
class CarSales(val name: String){ // 코틀린에는 정적(static) 변수 혹은 메소드가 없고, // 대신 패키지 내에 함수를 선언하여 사용할 수 있다. companion object { var total = 0 }
var count = 0 fun sales(){ total++ count++ } }
예제4
fun main(args: Array<String>){ var first = MyHtml.generateHtmlObject("first") first.setColumnHeaders("1열","2열","3열") //println("first.generateTable() => ${first.generateTable()}")
val second = MyHtml.generateHtmlObject("second", 5,5) second.setColumnHeaders("월","화","수","목","금")
val third = MyHtml.generateHtmlObject("third",2,7) third.setColumnHeaders("적색","녹색","노랑","파랑","보라","검정","주황")
object MyRecord { private var _count = 0 private val _tables = hashMapOf<String,MyHtml>()
fun gen(table: MyHtml){ _tables.put(table.name, table) _count++ } fun showTableCount() = _count fun showTable(): Map<String,MyHtml> = _tables }
class MyHtml { val name: String val row: Int val col: Int private val HEAD = "<table border='1' cell-spacing='10' cell-padding='10'" private val TAIL = "</table>" private lateinit var comment: String private val tableColumns = ArrayList<String>()
private constructor(_name: String, row: Int, col: Int ){ this.name = _name this.row = row this.col = col }
fun setColumnHeaders(vararg colNames: String){ this.tableColumns.addAll(colNames) }
companion object _GenTable { fun generateHtmlObject(_name: String, row: Int = 2, col: Int = 3): MyHtml { val mh = MyHtml(_name, row, col) mh.comment = "<!-- auto-generator table by MyHtml v1.0 -->" // 테이블 객체 기록 MyRecord.gen(mh) return mh } }
private inner class Tr { val rowHtml = """ |<tr> | ${"<td>-</td>".repeat(col)} |</tr>${"\n"} """.trimMargin() }
private inner class Th(val wpx: Int = 40) { val headers = tableColumns.map { "<th width='$wpx'> $it </th>" } val rowHtml = """ |<tr> | ${headers.joinToString("")} |</tr> """.trimMargin() }
// 테이블 생성 실행 fun generateTable() = """ |$comment |$HEAD |${Th().rowHtml} |${Tr().rowHtml.repeat(row)} |$TAIL """.trimMargin() }
코틀린에서는 접근자와 설정자를 따로 만들어줄 필요가 없다. 자바에서는 private 접근제한자로 은닉화된 멤버 변수에 getter/setter 메소드를 사용해서 접근한다. 코틀린은 property가 getter/setter를 대체한다.
코틀린에서 속성(property)란 최상위 변수(함수나 클래스 외부에 정의된 변수)나 클래스의 멤버 변수로 선언하면 속성으로 간주된다. 클래스의 멤버 변수는 모두 private 제한자로 지정된다. 따라서 해당 클래스의 내부의 getter와 setter를 통해서만 속성을 참조할 수 있다.
package com.myapp.funcargs
fun main(){ val a = Person("홍길동", 30) println(a) //println(a.toString())
val b = a.copy("이순신") println(b)
val c = b.copy(age=40) println(c)
val(name, age) = Person("강감찬",35) println("성명 : $name, 나이 : $age")
val obj = Pair<String, Int>("김좌진", 50) println(obj) }
// 데이터는 보유하지만 아무것도 안하는 클래스 data class Person(val name: String, val age: Int) // 파라미터 없는 기본 생성자가 필요한 경우에는 // data class Person(val name: String = "", val age: Int = 0) // 모든 프로퍼티에 기본 값을 설정해 주면 된다. // 기본 생성자에서 선언된 속성을 통해, 컴파일러가 자동으로 toString(), hashCode(), equals() 등을 생성한다. // 명시적으로 정의해준 경우에는 컴파일러가 자동으로 생성하지 않는다.
실행결과
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
@Parcelize
data class Memo_AddEdit (
var idx: String,
var memo: String,
var userID: String,
var userNM: String,
): Parcelable
class MainActivity : AppCompatActivity() {
private lateinit var mContext: Context
private lateinit var mMemo_AddEdit: Memo_AddEdit
override fun onCreate(savedInstanceState: Bundle?) {
고차함수(High-order Function)란 다른 함수를 인자로 사용하거나, 함수를 결과값으로 반환하는 함수를 말한다.
package com.myapp.funcargs
fun main(){ val a = sum(4,5) // 일반 인자 println("a result : $a") val b = mul(sum(1,2), 3) // 인자에 함수 사용 println("b result : $b")
println("ResultFun : ${resultFun()}") }
fun sum(a: Int, b: Int) = a + b fun mul(a: Int, b: Int) = a * b fun resultFun(): Int { return mul(3,5) // 함수의 반환 값으로 함수 사용 }
람다 표현식
람다식, 또는 람다 함수는 프로그래밍 언어에서 사용되는 개념으로 익명 함수 (Anonymous Functions)를 지칭하는 용어이다.
val lambdaName: Type = {argument -> codeBody}
람다 함수는 { } 안에 매개변수와 함수 내용을 선언하는 함수로 다음 규칙에 따라 정의한다. - 람다 함수는 항상 { }으로 감싸서 표현해야 한다. - { } 안에 -> 표시가 있으며 -> 왼쪽은 매개변수, 오른쪽은 함수 내용이다. - 매개변수 타입을 선언해야 하며, 추론할 수 있을 때는 생략할 수 있다. - 함수의 반환값은 함수 내용의 마지막 표현식이다.
fun sum(a: Int, b: Int) = a + b 를 람다 함수로 정의하면 val sum ={a: Int, b: Int -> a + b}
또는
val sum: (Int, Int) -> Int ={a, b -> a + b}
package com.myapp.funcargs
fun main(){ val a = sum(4,5) // 일반 인자 println("a result : $a") val b = mul(sum(1,2), 3) // 인자에 함수 사용 println("b result : $b")
println("ResultFun : ${resultFun()}")
val c:(String)->Unit ={str -> println("$str 람다함수")} b(c) }
val sum ={a: Int, b: Int -> a + b} // val sum: (Int, Int) -> Int ={a, b -> a + b} // val mul ={a: Int, b: Int -> a * b} val mul: (Int, Int) -> Int ={a, b -> a * b} fun resultFun(): Int { return mul(3,5) // 함수의 반환 값으로 함수 사용 } fun b (function: (String)->Unit){ function("b가 호출한") }
확장함수(Extension Function) Extension functions(확장 함수)는 기존에 정의된 클래스에 함수를 추가하는 기능이다. 자신이 만든 클래스는 새로운 함수가 필요할 때 쉽게 추가할 수 있다. 하지만 Standard Library 또는 다른 사람이 만든 라이브러리를 사용할 때 함수를 추가하기가 매우 어렵다.
확장 함수는 fun 클래스이름.함수이름(매개변수): 리턴타입 { 구현부 }으로 정의할 수 있다.
또는 fun 확장하려는대상.함수명(매개변수):Unit { }
fun 확장하려는대상.함수명(매개변수):리턴타입 { return 값 }
확장함수 예제1
class Car { fun getPrice() : Int { return 8000 } }
fun Car.getBrandName() : String { return "BMW" }
fun Car.getBrandName2() : Car { println("KIA") return this }
fun main(args: Array<String>) { val car = Car() println(car.getBrandName()) println(car.getBrandName2().getPrice()) }
확장함수 예제2
fun String.getLonggerLength(x: Int) : Int { return if(this.length > x) this.length else x }
fun main(args: Array<String>) { println("Hello".getLonggerLength(3)) }
fun main(args: Array<String>) { println("Hello".getLonggerStringLength("Have a nice day!")) }
fun String.getLonggerStringLength(x: String) : Int { return if(this.length > x.length) this.length else x.length }
확장함수 예제3
class Car { fun getPrice(): Int { return 10000 } }
fun Car.getPrice(): Int { return 20000 }
fun Car.getPrice(price: Int): Int { return 30000 }
fun main(args: Array<String>) { val car = Car() println(car.getPrice()) println(car.getPrice(0)) }
확장함수 예제4
fun List<Int>.getHigherThan(num: Int): List<Int> { val result = arrayListOf<Int>() for (item in this) { if (item > num) { result.add(item) } } return result }
fun main() { val numbers: List<Int> = listOf(1, 2, 3, 4, 5, 6, 7) val filtered = numbers.getHigherThan(3).toString() System.out.println(filtered) }
자바에서는 접근 제한자(Access Modifier)라는 용어를 사용하고 코틀린에서는 Visibility Modifier로 사용하는데, 우리말로는 접근 제한자 또는 가시성 제한자라고 할 수 있다. 코틀린에서 접근 제한자를 가질 수 있는 요소로는 class, object, interface, constructor, function, property 등이 있다.
자바에서는 접근 제한자를 붙이지 않으면 default(package-private) 이다. 그러나 코틀린에서는 아무것도 붙이지 않으면 public 이다.
코틀린에서 말하는 모듈은 자바와는 약간의 차이가 있는데, 자바에서의 모듈은 기능이 비슷한 클래스들의 집합체를, 코틀린에서는 동일한 컴파일의 집단을 의미한다.
패키지 레벨의 접근제한자
프로퍼티와 함수를 최상위(Top) 레벨에 작성한다는 것은 클래스에 멤버로 포함하지 않고 코틀린 파일에 정의하는 것을 의미한다.
package foo
public val myData: Int = 10 public fun myFun() {} public class myClass() {}
- 접근 제한자를 표기하지 않으면 public이 기본으로 설정되며, 누구나 접근이 가능하다.
- private : 해당 .kt 파일 내부에서만 사용 가능하다. - internal : 프로젝트의 모듈 안에서 누구나 접근이 가능하다. - protected : 최상위(top-level)에서는 사용할 수 없다.
클래스와 인터페이스의 접근제한자 클래스 또는 인터페이스 안에 선언되는 멤버들에 사용되는 접근 제한자는 다음과 같은 의미를 갖는다. - public : 누구나 접근이 가능하다. - private : 클래스 내부에서만 접근이 가능하다. - protected : 클래스 내부와 상속받는 객체에서 접근이 가능하다. - internal : 프로젝트의 모듈 안의 누구나 접근이 가능하다.
private, protected, public 과 Java와 코틀린에서 같은 의미로 사용된다. 하지만 internal은 자바에서는 default(같은 패키지내에서만 접근가능) 코틀린에서는 같은 모듈안에서만 사용 가능하다.
생성자의 접근제한자 생성자에도 접근제한자를 붙일 수 있다. 기본은 public 생성자로 누구나 접근이 가능하다. internal을 붙이면 모듈 내에서만 접근 가능하다. private을 붙이면 클래스 내에서만 접근이 가능하여 외부에서 생성할 수 없다.
// private 생성자 class Person private constructor(a: Int) { }
Java 접근 제한자
접근 제한자는 Class 의 멤버에 대해 다른 Class 에서 접근하는 것을 제한하는 용도로 사용된다.
- public : 모든 Class 에서 접근 가능
- protected : 같은 패키지에 있는 Class, 그리고 다른 패키지의 자손 클래스에서 접근이 가능
- default : 같은 패키지에 있는 Class 들만 접근 가능
String name; // 아무런 접근 제한자도 지정되지 않았으므로 default 접근 제한자다.
코틀린에서 프로젝트(Project)는 모듈(Module), 패키지(Package), 파일(File)로 구성되어 있다. 프로젝트에는 모듈이 있고 모듈은 다시 패키지로 구성되어 있다.
Java는 파일명과 클래스명이 동일해야 하지만, 코틀린은 public 클래스는 하나만 사용 해야하는 규칙이 없다.
코틀린 파일은 .kt 확장자를 가지며 맨 위에는 이 파일이 어떤 패키지에 포함된 것인지 코틀린 컴파일러가 알 수 있도록 패키지 이름을 선언해야 한다. 만약 패키지 이름을 선언하지 않으면 그 파일은 자동으로 default 패키지에 포함된다. 파일이 패키지 안에 들어있어도 패키지 이름을 선언하지 않으면 default 패키지에 포함된 것으로 인식한다. 파일에 클래스가 여러 개 정의되어 있다면 파일은 단순히 클래스를 묶는 역할을 하고 kt 확장자가 붙게 된다. 코틀린에서는 파일명과 클래스의 선언 개수에 큰 의미를 두지는 않는다. 같은 파일에 있는 여러 개의 클래스는 모두 그 파일에서 지정한 패키지로 인식한다.
패키지 이름은 파일 맨 위에 적는다. 이때 패키지 이름 앞에 package라는 키워드를 함께 입력해야 패키지 이름으로 인식한다. 단, 패키지의 이름은 특수 문자나 숫자로 시작하면 안된다.
abstract class Person(name: String) { init { println("My name is $name.") } // 일반 메서드 fun displaySSN(ssn: Int) { println("My SSN is $ssn.") } // 추상 메서드 abstract fun displayJob(description: String) }
class Teacher(name: String): Person(name) { // 추상 메서드 구현 override fun displayJob(description: String) { println(description) } }
fun main(args: Array<String>) { val jack = Teacher("Jack Smith") jack.displayJob("I'm a mathematics teacher.") jack.displaySSN(23123) }
코틀린은 자바, C++과는 달리 클래스 선언 부분에서 이름에 괄호를 붙여 생성자를 만들 수 있다.
자바는 멤버변수를 field라고 하지만 코틀린 프로퍼티라는 용어를 사용한다. 자바는 생성자를 꼭 정의해야 하지만 코틀린은 생략이 가능하다.
코틀린에는 주 생성자와(primary constructor)와 부생성자가(secondary constructor)가 존재한다. 주 생성자는 class 선언과 함께 선언하고, 부 생성자는 추가적인 생성자가 필요할 때 사용한다. 생성자를 정의하지 않으면 기본으로 인자가 없는 생성자를 만들어 준다.
부모의 생성자는 super(), 클래스 내 생성자중 다른 생성자는 this()로 호출이 가능하다.
fun main() { var a = Person("박보영", 1990) //코틀린은 new 키워드 없이 인스턴스를 생성한다. var b = Person("전영경",1997) var c = Person("차은우") var d = Person("홍길동")
// 클래스 별로 여러 개를 가질 수 있다. constructor(name: String) : this(name,1997) }
기본 생성자
- 기본 생성자에 어노테이션 접근지정자 등이 있을 경우 constructor 키워드가 필요하다.
class Person public @Inject constructor(name: String) { .... }
클래스 상속
- 코틀린의 최상위 클래스는 Any 이다.
Any는 java.lang.Object 와는 다른 클래스이며, equals(), hasCode(), toString()만 있다.
- 상속 키워드는 콜론(:)이다. Java 의 상속 키워드는 extends
- 코틀린의 클래스와 메소드는 기본적으로 final이다. 즉 기본적으로 상속을 못하게끔 되어 있다.
Java에서는 클래스나 메서드에 final 키워드를 붙여 클래스를 더 이상 상속받지 못하게 하거나,
메서드를 재정의하지 못하게 할 수 있다. - 클래스의 상속을 허용하려면 클래스 앞에 open 변경자를 붙여야 한다.
open 변경자가 없으면 final 로 자식 클래스에서 override가 불가능하다.
오버라이드를 허용하고 싶은 메소드나 프로퍼티 앞에도 open 변경자를 붙여야 한다.
- 부모 클래스의 메소드를 자식 클래스에서 오버라이드 한 경우, 해당 메소드는 기본적으로 열려있다.
fun main() { var a = Animal("불독", 3,"개") var b = Dog("불독", 5)
a.introduce() b.introduce() b.bark()
var c = Cat("나갈래", 4) c.introduce() c.meow() }
open class Animal(var name:String, var age:Int, var type:String){ open fun introduce(){ // open 이므로 override 가능하다. println("저는 ${type} : ${name}, ${age} 살 입니다.") } }
class Dog(name:String, age:Int) : Animal (name,age,"개"){ // 상위 클래스 함수에 open 키워드를 명시하고, 서브 클래스 함수에 override 를 붙인다. override fun introduce(){ println("타입 : 개, ${name}, ${age} 살 입니다.") }
fun bark(){ println("멍멍멍") } }
class Cat(name: String, age: Int) : Animal(name, age, "고양이"){ fun meow() { println("야옹야옹") } }
fun foo2() { var ints = listOf(0, 1, 2, 3) // 람다식에서 return 시 nearest enclosing 함수가 return 된다. ints.forEach { if(it == 1) return print(it) } print("End") }
fun foo3() { var ints = listOf(0, 1, 2, 3) // 람다식에 대해서만 return 하려면 label 을 이용해야 한다. ints.forEach loop@{ if(it == 1) return@loop print(it) } print("End") }
fun foo4() { var ints = listOf(0, 1, 2, 3) // 람다식에 대해서만 return 하려면 label 을 이용해야 한다. // 암시적 레이블은 람다가 사용된 함수의 이름과 동일하다. ints.forEach { if(it == 1) return@forEach print(it) } print("End") }
fun foo5(): List<String> { var ints = listOf(0, 1, 2, 3) val result = ints.map{ if(it == 0){ return@map "zero" } "No $it" } return result }