JAVA 를 베이스로 한 코틀린을 예시코드 통해 학습 해봅시다!
● 스코프
스코프(scope)란 변수나 함수, 클래스 같은 멤버들을 서로 공유하여 사용할 수 있는 범위를 지정되는 단위
○ 패키지 스코프
public - 어떤 패키지에서도 접근 가능
internal - 같은 모듈 내에서만 접근 가능
private - 같은 파일 내에서만 접근 가능
protected 는 패키지 스코프에서 사용 X
○ 클래스 스코프
public - 클래스 외부에서도 접근 가능
private - 클래스 내부에서만 접근 가능
protected - 클래스 자신과 상속받은 클래스에서 접근 가능
internal은 클래스 스코프에서 사용 X
// 스코프(scope)란 변수나 함수, 클래스 같은 멤버들을
// 서로 공유하여 사용할 수 있는 범위를 지정되는 단위
// 범위 : 패키지, 클래스, 함수
// 규칙 1 - 스코프 외부에서는 스코프 내부의 멤버를 참조연산자로만 참조가 가능
// 규칙 2 - 동일 스코프 내에서는 멤버들을 공유 가능
// 규칙 3 - 하위 스코프에서는 상위 스코프의 멤버를 재정의 가능하위 스코프에서는 상위 스코프의 멤버를 재정의 가능
val a = "패키지 스코프"
class B{
val a = "클래스 스코프"
fun print(){
println(a)
}
}
fun main() {
val a = "함수 스코프"
println(a)
B().print()
}
● 고차함수
클래스에서 만들어낸 인스턴스처럼 취급하는 방법, 모든 함수는 고차함수로 사용 가능
함수 타입 | 의미 | 예제 코드 |
() -> Unit | 아무것도 안 받고, 아무것도 반환 안 함 | { println("Hello") } |
() -> String | 아무것도 안 받고, String 반환 | { "Hello" } |
(String) -> Unit | String 1개 받고, 반환 없음 | { str -> println(str) } |
(String) -> String | String 1개 받고, String 반환 | { str -> str.uppercase() } |
(Int, Int) -> Unit | Int 2개 받고, 반환 없음 | { a, b -> println(a + b) } |
(Int, Int) -> Int | Int 2개 받고, Int 반환 | { a, b -> a + b } |
(String, Int) -> Boolean | String, Int 받고 Boolean 반환 | { name, age -> age > 18 } |
// 함수 순서
// 1. main 함수가 a 함수를 b 함수에 파라미터로 넘김
// 2. b 함수는 받아온 a 함수에 값을 넘겨서 호출
// 3. 최종적으로 a 함수가 호출
fun main() {
// 함수 b를 호출하되 함수 a 를 파라미터로 넘김
// 고차함수로 넘기려면 함수 이름 앞에 ::(콜론) 두개를 붙여주면 됨
b(::a)
val c : (String)->Unit = { str -> println("$str 람다함수")}
b(c)
}
fun a (str : String){
println("$str 함수 a")
}
// 함수를 인스턴스로 받을 때 [(자료형)->반환형] 으로 만들 수 있음
fun b (function : (String)->Unit){
function("b가 호출한")
}
● 스코프 함수
함수형 언어의 특징을 좀 더 편하게 사용할 수 있도록 기본 제공하는 함수들
// with - run과 동일한 기능, 단지 인스턴스를 참조연산자 대신 파라미터로 받는다는 차이
// also / let - apply / run과 같은 역할, 하지만 함수 내에 중복 변수가 선언되어있을 경우 값의 혼돈을 막기 위할 때 사용
// (파라미터로 인스턴스를 넘긴 것처럼 it 을 통해 인스턴스를 사용)
fun main() {
var price = 5000
// apply - 인스턴스를 생성한 후 변수에 담기전 초기화 과정을 수행할 때 사용
var a = Book("우루쾅의 코틀린", 10000).apply{
name = "[초특가] " + name
discount()
}
// run - 이미 인스턴스가 만들어진 후 인스턴스의 함수나 속성을 scope 내에서 사용해야할 때 사용
a.run{
println("${name} 가격 ${price}")
}
a.let{
println("${it.name} 가격 ${it.price}")
}
}
class Book(var name:String, var price: Int){
fun discount(){
price -= 2000
}
}
● object(싱글톤 객체)
object 로 생성된 객체는 최초 사용시 자동으로 생성되며 이후 코드 전체에서 공용으로 사용될 수 있음
fun main() {
println(Counter.count)
Counter.countUp()
Counter.countUp()
println(Counter.count)
Counter.clear()
println(Counter.count)
}
// 싱글톤 객체
// object 로 생성된 객체는 최초 사용시 자동으로 생성되며
// 이후 코드 전체에서 공용으로 사용될 수 있음
object Counter{
var count = 0
fun countUp(){
count++
}
fun clear(){
count = 0
}
}
● companion (공유 객체)
서로 다른 인스턴스 임에도 companion object 를 공유하고 있기 때문에 모든 인스턴스에서 투표 수를 누적할 수 있음
클래스 내의 인스턴스를 사용하면서 공용으로 만드는 기능, 기존의 Static 클래스와 유사함
fun main() {
var a = FoodPoll("짜장")
var b = FoodPoll("짬뽕")
a.vote()
a.vote()
b.vote()
b.vote()
b.vote()
println("${a.name} : ${a.count} ")
println("${b.name} : ${b.count} ")
// 서로 다른 인스턴스 임에도 companion object 를 공유하고 있기 때문에
// 모든 인스턴스에서 투표 수를 누적할 수 있음
println("전체 갯수 : ${FoodPoll.total}")
}
class FoodPoll (val name:String){
// 클래스 내의 인스턴스를 사용하면서 공용으로 만드는 기능
// 기존의 Static 클래스와 유사함
companion object {
var total = 0
}
// 일반 사용할 함수는 companion 외부에 선언
var count = 0
fun vote(){
total++
count++
}
}
● observer 패턴
함수를 직접 선언하지는 않았지만 이벤트 발생시 즉각적으로 처리할 수 있도록 만드는 프로그래밍 패턴
이벤트 수신 + 이벤트 발생 및 전달
fun main() {
// 1. start 함수 호출
EventPrinter().start()
}
// observer 패턴
// 함수를 직접 선언하지는 않았지만 이벤트 발생시
// 즉각적으로 처리할 수 있도록 만드는 프로그래밍 패턴
// 이벤트 수신 + 이벤트 발생 및 전달
interface EventListener{
fun onEvent(count : Int)
}
class Counter(var listener : EventListener){
fun count(){
for(i in 1..100){
// 3. 동작 진행 중 조건 맞을 시 onEvent 함수 호출
if(i % 5 == 0) listener.onEvent(i)
}
}
}
class EventPrinter : EventListener{
override fun onEvent(count : Int){
print("${count} - ")
}
// 2. 함수에서 counter 멤버의 count() 호출
fun start(){
val counter = Counter(this)
counter.count()
}
}
● 익명객체
아래코드에서는 Counter(익명객체) 로 사용
인터페이스를 구현한 객체를 코드 중간에서 즉시 생성하여 사용할 수 있음
fun main() {
EventPrinter().start()
}
interface EventListener{
fun onEvent(count : Int)
}
class Counter(var listener : EventListener){
fun count(){
for(i in 1..100){
if(i % 5 == 0) listener.onEvent(i)
}
}
}
class EventPrinter {
fun start() {
// 익명객체
// 아래코드에서는 Counter(익명객체) 로 사용
// 인터페이스를 구현한 객체를 코드 중간에서
// 즉시 생성하여 사용할 수 있음
val counter = Counter(object : EventListener {
override fun onEvent(count : Int){
print("${count} - ")
}
})
counter.count()
}
}
● 리스트
- List<out T>
생성시에 넣은 객체를 대체, 추가, 삭제 할 수 없음
- MutableList<T>
생성시에 넣은 객체를 대체, 추가, 삭제가 가능함
fun main() {
// List<out T>
// 생성시에 넣은 객체를 대체, 추가, 삭제 할 수 없음
val a = listOf("사과","딸기","배")
println(a[1])
for(fruit in a){
println("${fruit} : ")
}
// MutableList<T>
// 생성시에 넣은 객체를 대체, 추가, 삭제가 가능함
val b = mutableListOf(6, 3, 1)
println(b)
b.add(4)
println(b)
b.add(2, 8)
println(b)
b.removeAt(1)
println(b)
b.shuffle()
println(b)
b.sort()
println(b)
}
● null 처리 방식
○ ?.
참조연산자를 실행하기 전에 객체가 null 인지 확인부터 하고 객체가 null이면 뒤의 연산자를 사용하지 않음
○ ?:
객체가 null 이 아니라면 그대로 사용하지만 null 이라면 연산자 우측의 객체로 대체됨
○ !!.
참조연산자를 사용할 때 null 여부를 컴파일 시 확인하지 않도록 하여 런타임시 NPE 발생하도록 방치하는 연산자
fun main() {
var a: String? = null
// ?.
// 참조연산자를 실행하기 전에 객체가 null 인지 확인부터 하고
// 객체가 null이면 뒤의 연산자를 사용하지 않음
println(a?.uppercase())
// ?:
// 객체가 null 이 아니라면 그대로 사용하지만
// null 이라면 연산자 우측의 객체로 대체됨
println(a?:"default".uppercase())
// !!.
// 참조연산자를 사용할 때 null 여부를 컴파일 시 확인하지 않도록 하여
// 런타임시 NPE 발생하도록 방치하는 연산자
println(a!!.uppercase())
}
출처
코틀린 - https://play.kotlinlang.org/
디모의 Kotlin 강좌 - https://www.youtube.com/watch?v=8RIsukgeUVw&list=PLQdnHjXZyYadiw5aV3p6DwUdXV2bZuhlN&index=1&pp=iAQB
'개발지식' 카테고리의 다른 글
Kotlin 기본 구조(예시 코드) (8) | 2025.08.14 |
---|---|
Kotlin 기본 문법(예시 코드) (5) | 2025.08.11 |
포트포워딩(port forwarding)으로 내부망 PC 접근하기(포트포워딩 설정방법) (4) | 2025.07.28 |
(JPA) mappedBy 완벽 이해! (8) | 2025.07.26 |
(JPA) 연관관계 매핑 (3) | 2025.07.25 |