
Kotlin에서는
vararg키워드를 사용하여 하나의 매개변수에 여러 개의 값을 전달할 수 있다.
* 연산자 (스프레드 연산자)를 붙여 전달할 수도 있다.fun printCartList(size: Int, vararg items: String) {
println("$size 크기의 장바구니에 ${items.joinToString(", ")}(이)가 들어있습니다.")
}
printCartList(4, "성찬", "앤톤", "은석", "쇼타로", "원빈", "소희")
val cartList = arrayOf("감자", "고구마", "계란", "파")
printCartList(cartList.size, *cartList)
String인데, cartList는 배열 타입이다. 그럴 때는 스프레드 연산자를 사용해서 풀어 넣어줘야 한다.fun logging(type : String = "INFO", vararg msg : String) {
for (s: String in msg) {
println("[${type}] $s")
}
}
logging(msg=arrayOf("로그1", "로그2", "로그3"))
for문에서 스마트 캐스트로 인해 array로 넣어도 가능한 것이다.Kotlin은 함수형 프로그래밍과 객체지향 프로그래밍을 모두 지원하는 다중 패러다임 언어이다.
함수형 프로그래밍은 순수 함수를 작성하여 프로그램의 부작용을 줄이는 프로그래밍 기법을 의미한다.
입력이 같으면 항상 같은 값을 반환하고, 함수가 함수 외부의 어떤 상태도 바꾸지 않는다.
// 순수 함수
fun power(x:Int) {
return x*x;
}
// 순수 함수 X, 비순수 함수
var counter: Int = 0
fun increaseCounter(x:Int) {
counter++
return x + counter
}
함수형 프로그래밍에서는 함수를 일급 객체로 취급한다. 일급 객체란 매개변수, 함수의 반환값, 할당 명령문의 대상, 동일 비교의 대상이 될 수 있는 것들을 의미한다.
다른 함수를 인자로 사용하거나 함수를 결과값으로 반환하는 함수를 의미한다. 일급 객체를 서로 주고받을 수 있는 함수가 고차함수가 될 수 있게 된다.
fun times(x:Int) : (Int) -> Int {
return { target -> x * target }
// 매개변수 -> 반환값
}
fun main() {
val fiveTimes =times(5)
val threeTimes = times(3)
println("fiveTimesFive = ${fiveTimes(5)}") // 25
println("threeTimesFive = ${threeTimesFive(5)}") // 15
}
익명 함수는 이름 없이 바로 정의해서 사용할 수 있는 함수이다.
fun키워드를 사용하지만 별도의 함수 이름 없이, 주로 짧은 동작을 전달하거나 일회성 작업을 할 때 사용된다.
fun main() {
val process1 = fun(v:String) { // 익명함수
println(v)
}
process1("abc") // abc
val process2 = fun(v: String): String {
return "processed $v"
}
process2("abc") // processed abc
}
val sayHello: () -> Unit = fun() {
println("Hello World!")
}
sayHello()
// Hello World!
fun main() {
val sum = { x:Int, y:Int -> // 람다 표현식 매개변수
println("입력한 x = ${x}, y = $y")
}
sum(1,2)
}
fun main() {
val sum = { x:Int, y:Int -> x + y }
val sumResult = sum(5,5)
println(sumResult) // 10
}
{} 안에 화살표 연산자->를 사용하여 표현하는 것이 특징이다.val ap: (Int, Int) -> Int = ap@{ x, y ->
if (x > y) {
return@ap x
}
x + y
}
val app1 = ap(100, 200)
val app2 = ap(200, 100)
println("app1 = ${app1}") // 300
println("app2 = ${app2}") // 300
x+y)하기 때문에, 라벨을 붙여주기 전까지는 300으로 같은 값을 리턴한다.return을 사용할 수 있다.후행 람다 함수
fun main() {
sayHello("성찬") {
println("반갑습니다!")
}
}
fun sayHello(target:String, greeting: () -> Unit) {
println("${target}님, ")
greeting()
}
인라인 함수는 함수 호출을 줄이기 위하여 컴파일 시 함수 본문을 호출한 곳에 그대로 복사하는 방식으로 동작하는 함수이다. 함수를 호출할 때는 호출 스택에 함수가 올라가게 되는데, 그 과정이 필요없게 되는 것이다. 인라인 함수는
inline이라는 키워드를 함수 정의 키워드 fun 앞에 붙이는 방식으로 사용한다.
fun main() {
numbering(1) { n ->
println("입력받은 숫자 : $n")
}
inline fun numbering(number:Any, func:(Any) -> Unit) {
println("숫자세기 시작!")
func(number)
println("숫자세기 끝!")
}
}
위의 함수를 호출하게 되면 아래와 같아진다.
fun main() {
var number = 1
println("숫자세기 시작!")
println("입력받은 숫자 : $number")
println("숫자세기 끝!")
inline fun numbering(number:Any, func:(Any) -> Unit) {
println("숫자세기 시작!")
func(number)
println("숫자세기 끝!")
}
}
중위 표현법은 메서드를 마치 연산자처럼 사용할 수 있게 해주는 문법이다. infix 키워드를 함수 앞에 붙이는 방식으로 사용할 수 있으며, infix가 추가되어있으면 객체와 객체 사이에 함수 이름을 두고 호출할 수 있게 된다.
class Person(val name: String) {
infix fun eat(sth: String) {
println("$name 는 ${sth}을(를) 먹습니다!")
}
}
val person1 = Person("성찬")
person1 eat "사과"
// 성찬 는 사과을(를) 먹습니다!
같은 이름의 함수이지만, 함수 시그니처가 동일하지 않은 함수를 구현하는 것을 의미한다.
fun sum(a: Int, b:Int):Int {
return a + b
}
fun sum(a:Double, b:Double) :Double {
return a + b
}
캡슐화(Encapsulation)
객체의 데이터와 그 데이터를 처리하는 메서드를 하나로 묶어 외부로부터 객체의 내부 구현을 숨기는 원칙이다.
- 객체의 상태(데이터)는 외부에서 직접 접근하거나 변경할 수 없으며, 객체가 제공하는 공용 메서드를 통해서만 데이터를 조작할 수 있다.
- 외부에는 꼭 필요한 기능만 공개(public)하고, 나머지는 감추는 방식이다.
상속(Inheritance)
기존의 클래스(부모 클래스)로부터 새로운 클래스(자식 클래스)를 생성하여, 부모 클래스의 속성과 메서드를 자식 클래스가 재사용할 수 있도록 하는 원칙이다.
- 자식 클래스는 부모 클래스의 모든 특성을 상속받으며, 이를 기반으로 추가적인 속성이나 메서드를 정의하거나, 기존의 메서드를 수정(오버라이딩)할 수 있다.
다형성(Polymorphism)
같은 타입의 객체가 상황에 따라 다른 동작을 하는 성질을 말한다.
- 메서드 오버라이딩(Override)과 인터페이스 구현(Interface Implementation)을 다형성의 대표적인 예시이다.
추상화(Abstraction)
복잡한 시스템의 중요한 부분만을 강조하고, 불필요한 세부 사항은 숨기는 원칙이다.
- 객체가 공통적으로 수행할 수 있는 기능을 정의하고, 이 기능에 대한 구체적인 세부 사항은 숨긴다.
- Kotlin에서는 추상 클래스(abstract class)와 인터페이스(interface)를 통해 추상화를 구현할 수 있다.
class 클래스이름 { // 클래스 선언
// 정의
val field1 = "value" // 속성 (Field)
fun behavior(): Unit {
} // 함수 (Method)
}
클래스라는 설계도를 바탕으로 생성된 구체적인 객체(Object)를 의미한다.
객체를 초기화하는 특별한 종류의 메서드이다. 생성자는 클래스를 인스턴스화할 때 호출하며 객체의 초기 상태를 설정하는 데에 사용된다.
class Car constructor(name:String, c
,color : String
,size : Int
, isGasoline:Boolean) {
var name: String = name
var color: String = color
var size: Int = size
var isGasoline :Boolean = isGasoline
}
주 생성자
주 생성자는 클래스 이름과 함께 정의되는 생성자로서, constructor를 생략하여 작성하는 것도 가능하다.
class Car (var name: String
, var color: String
, var size: Int
, var isGasoline: Boolean) {
}
var 혹은 val을 추가한다면 클래스 내부에서는 속성(Property)를 생략할 수 있다. 부 생성자 (Secondary Constructor)
일번 메서드와 마찬가지로 클래스의 본문에 함수와 같이 선언할 수 있다. 그리고 필요하다면 매개변수를 다르게 하여 여러 개 오버로딩 하는 것이 가능하다.
class Car {
var name: String
var color: String
var size: Int
var isGasoline: Boolean
constructor(name: String, color: String, size: Int, isGasoline: Boolean) {
this.name = name
this.color = color
this.size = size
this.isGasoline = isGasoline
}
}
init 블록
init 블록은 Kotlin 클래스에서 객체가 생성될 때 자동으로 실행되는 초기화 블록이다. 주생성자가 호출될 때 클래스가 생성되면서 init 블록 안에 작성된 코드가 가장 먼저 실행된다. 이 블록은 주생성자에게서 받은 매개변수를 검증하거나, 복잡한 초기화 작업이 필요한 경우에 주로 실행된다.
class Car (var name: String
, var color: String
, var size: Int
, var isGasoline: Boolean) {
init {
println("""
자동차가 생성되었습니다!
자동차 이름: $name
자동차 색상: $color
자동차 크기: $size
주유 타입: ${if (isGasoline) "가솔린" else "디젤"}
""".trimIndent())
}
}
fun main() {
Car("붕붕이", "검정색", 100, true)
// 자동차가 생성되었습니다!
// 자동차 이름: 붕붕이
// 자동차 색상: 검정색
// 자동차 크기: 100
// 주유 타입: 가솔린
}
클래스가 생성된 후 해당 객체가 가지고 있는 고유한 값이나 상태를 의미한다. 이러한 속성은 클래스 안에 정의된 변수로 표현된다.
정보 은닉화란 객체 지향 프로그래밍에서 객체의 내부 상태나 구현 세부사항을 외부로부터 숨기는 개념이다. 외부에서는 객체가 어떤 방식으로 데이터를 저장하거나 처리하는지 알 필요가 없으며, 단지 제공된 인터페이스만을 통해 기능을 이용하면 된다.
Getter와 Setter를 사용하는 것이 정보 은닉화의 대표적인 방법인데, Kotlin에서는 속성에 대해 기본적으로 Getter와 Setter를 자동으로 생성해준다. class Member(_id: Int, _name: String, _nickname: String) {
val id: Int = _id
var name: String = _name
var nickname: String = _nickname
}
fun main() {
val memberA = Member(1, "admin", "admin")
// Getter
println("memberA id = ${memberA.id}")
// Setter
// memberA.id = 2 // Member 클래스의 id가 val로 되어있기 때문에 Setter 이용 불가능
memberA.name = "memberA"
memberA.nickname = "memberA"
// Getter
println("memberA name = ${memberA.name}")
println("memberA nickname = ${memberA.nickname}")
}
val와 var에 따라 생성되는 getter와 setter가 다르게 동작한다. val : 읽기 전용 속성, getter만 생성, setter 생성 xvar : 읽기, 쓰기 모두 가능, getter와 setter 모두 생성 oclass Member(_id: Int, _name: String, _nickname: String) {
val id: Int = _id
get() {
return field
}
var name: String = _name
get() {
return field
}
set(value) {
field = value
}
var nickname: String = _nickname
get() {
return field
}
set(value) {
field = value
}
}
field는 해당 속성의 실제 값을 가리키는 백킹 필드(backing field)이다.field 키워드를 사용해 속성 값을 직접 참조해야 한다. value는 Setter 내부에서 자동으로 제공되는 변수로, 새로 할당되는 값을 의미한다.객체의 속성을 나중에 초기화하고 싶을 때 지연 초기화(late initialization) 기능을 사용할 수 있다. 지연 초기화는
lateinit키워드로 수행할 수 있다.
lateinit 키워드는 var로 선언된 non-null 타입 프로퍼티에만 사용 가능하며, 주 생성자나 초기화 블록에서 바로 초기화되지 않고 나중에 값을 할당할 수 있게 해준다.class Animal {
lateinit var name: String
fun sayName() {
if ( ::name.isInitialized ) {
println("제 이름은 $name 이에요!")
} else {
println("아직 이름이 없습니다!")
}
}
}
fun main() {
val animal = Animal()
animal.sayName() // 아직 이름이 없습니다!
animal.name = "나비"
animal.sayName() // 제 이름은 나비 이에요!
}
lateinit 변수를 초기화하지 않고 사용하면 UninitializedPropertyAccessException 예외가 발생한다. lazy
val을 이용하여 lateinit과 같이 지연초기화를 할 때 사용할 수 있다.
class Animal {
val name: String by lazy {
println("이름 짓는 중...")
"나비"
}
}
fun main() {
val animal = Animal()
println("animal.name = ${animal.name}") // 이 시점에서 초기화
println("animal.name = ${animal.name}") // 이미 초기화된 값 사용
/*
이름 생성중..!
animal.name = 나비
animal.name = 나비
*/
}
lazy는 해당 프로퍼티가 처음 호출될 때 딱 한 번 실행되고 이후에는 캐시된 값을 반환하는 방식으로 동작한다.가시성(Visibility)이란 코드에서 어떤 요소(클래스, 함수, 변수 등)가 다른 코드에 의해 보일 수 있는 범위를 의미한다. 즉, 어떤 코드 조각이 외부에서 접근 가능한지, 아니면 내부에서만 사용할 수 있는지를 구분하는 개념이다.
public, internal, protected, private 같은 가시성 지시자를 제공하며, 각각 접근 범위를 다르게 설정할 수 있다.
private: 외부에서 접근 불가능public: 어디서든지 접근 가능protected: 상속받은 하위 요소에서만 접근 가능internal: 같은 정의의 모듈 내부에서 접근 가능
내용이 굉장히.. 많다. 팀원 분 중 한 분이 오늘 내용 고봉밥이라고 했는데,, 동의요 ㅎ.. 뭐랄까 이해는 되는데, 흡수해야 할 내용들이 많아서 좀 힘들었던 것 같다.
그치만 복습하고나니까 이해도 잘 되고, 괜찮은 것 같다 !
근데 솔직히 함수를 왜 저렇게 쓰지.. 싶은 게 많다. 그냥 다 fun 하면 안될까? 왜 굳이 굳이 익명함수를 쓰고 그걸 변수에 넣어서.. 그 변수를 호출하는 식으로 쓰는걸까... 하하하하
아직 화요일이라는 것을 난 믿을 수 없다. 믿길 거부한다 !!!! 악!