23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
Kotlin 은 JVM 과 완벽하게 호환되는 언어 입니다.
Java 와 100% 상호호환을 하는 것을 목표로 하는 Kotlin 은 Java 언어를 아시면 보다 쉬운 이해가 가능 합니다.
Java 보다 더 간결한 언어를 사용할 수 있고 1급 함수를 지원하기 때문에 함수형 프로그래밍을 보다 적극적으로 사용할 수 있는 장점이 있습니다.
또한 Google Android 공식언어로 Kotlin 이 추가되었고 이에 편승하여 많은 인기를 얻고 있습니다.
Kotlin 은 2011년 경 부터 개발을 진행해서 2016년 2월 V 1.0 이 출시 되었습니다. (현재는 1.9.24 버전이 최신 입니다)
Kotlin 은 만든 회사는 여러분들이 잘 아실 만한 JetBrain 입니다. (Intelli-J를 만든 회사죠)
[JetBrain 사의 회사 로고]
Kotlin 이란 이름은 JetBrain 의 개발센터가 있는 러시아의 상트페리트루르크에 있는 코틀린 섬에서 이름을 따왔다고 합니다.
(참고로 JetBrain 은 체코의 회사 입니다). 우리는 언제쯤 "월미도", "제주도" 이런 프로그램을 만들 수 있을까요?
[블로그 내용과 별 상관이 없는 코틀린 섬의 지도]
Kotlin 은 Java 와 100% 호환을 목표로 하고 있습니다.
그렇기에 기존 Java Library 를 그대로 사용할 수 있으며, 반대로 Java 에서도 Kotlin 을 사용할 수 있습니다.
Kotlin 은 일반적으로 서버상의 코드(웹 애플리켕션의 Back-End), 안드로이드 디바이스에서 실행되는 모바일 애플리케이션에 사용할 수 있습니다.
그리고 특이하게 자바 스크립트를 지원해서 브라우저나 Node 에서도 실행이 가능 합니다.
Java 와 마찬가지로 컴파일에 타입을 검사하는 정적 타입을 지원합니다.
하지만, Java 와 다른 점은 타입 추론 (type infernece) 를 통해서 변수의 타입을 생략 할 수도 있습니다
보통, 정적이라함은 Compile 시에 동작하는 것을 의미하고 동적이라 함은 코드가 실제로 실행되는 Runtime 시에 동작하는 것을 의미 합니다.
Java에서도 Functional Interface 와 Lambda 를 통해 일부 함수형 프로그래밍을 지원하지만 Kotlin 에서는 보다 완벽하게 함수형 프로그래밍을 지원합니다.
즉, 함수를 파라미터로 받거나 함수를 반환할 수 있습니다.
함수형 프로그래밍의 특징은 First-Class, immutability, non side-effect 등이 있습니다
컴파일러, 라이브러리 및 도구등이 모두 오픈 소스이며 Apache2 라이선스를 사용해서 무료료 사용할 수 있습니다.
Intelli-J Commucation 버전 및 Ultimate(이건 구매해야 되죠) 지원이 가능합니다.
Kotlin 에서는 val, var 라는 변수 타입을 지원합니다.
먼저 val 은 value 의 약자이며 Java 의 final 변수와 동일합니다.(한번 값이 지정되면 변경 불가능)
var 는 variable 의 약자이며 Java 의 final 이 아닌 변수와 동일합니다.(값 변경 가능)
kotlin 에서는 언어적 특성상 변수를 가급적 val 로 선언하는 것을 권고하고 있습니다.
변수명 뒤에 : 를 입력하고 타입명을 적어 주시면 됩니다.
val name: String 변수의 이름은 name 이고 타입은 String 입니다. (식의 마지막 ; 는 생략 가능합니다, 코드에서는 생략했지만 final 이기 때문에 ``값을 무조건 대입해줘야 합니다)
val name = "David" "David" 은 명시적으로 타입이 String 이기 때문에 타입 생략이 가능 합니다.
name = "James name을 윗줄의 코드에서 val 로 지정했기 때문에 다른 이름을 지정하면 컴파일 오류가 발생합니다.
java 에서는 항상 매개변수를 사용하기전에 null 체크를 해줘야 합니다.
Java8 이후부터는 Optional 이 도입되면서 null 체크에 보다 유연성이 생겼지만, 그래도 이 부분은 항시 신경을 써야 합니다.
Kotlin 에서는 변수 선언시에 이 변수가 null 을 허용할 것인지, 안 할 것인지를 선언할 수 있습니다.
var age: Int? = 변수의 이름은 age 이며 타입 뒤에 ? 를 표시해서 Null 이 들어올 수도 있습니다.
**Safe call **: perspon.age 와 같이 프로퍼티에 직접 접근할 경우 age 가 Null 이면 NPE(Null Point Exception)이 발생할 수 있씁니다.
safe call 은 perspon?.age ? 를 사용해서 프로퍼티에 접근하며, 만약 프로퍼티가 null 일 경우에는 Null 을 반환해 줍니다. (null 이 아닐 경우에는 프로퍼티의 값을 반환하겠죠)
Elvis 연산자 : Java 에서는 Ternary Expression 이 있습니다. ? : 를 사용해서 식을 값으로 표현할 수 있는데, Kotlin 에서는 Elvis 연산자를 사용해서 간단하게 표현이 가능합니다.
Elvis 연산자는 좌항의 식이 null 을 반환하면 우항의 값을 반환하게 표현할 수 있습니다.
return person?.age ?: throw IllegalArgumentException("나이가 null 입니다")
위 예제에서는 Safe Call 을 사용해서 age가 Null 일 경우 null 을 만들어내고, 엘비스 연산자(?:) 의 좌항이 null 이 됐기 때문에 연산자의 우항인 IllegalArgumentException 이 발생합니다.
참고로, Elvis 연산자는 엘비스 프레슬리의 머리모양을 따서 만들었다고 합니다.
[왜 엘비스인지 아직도 이해가 안되는 그림]
Kotlin 에서는 대부분의 문(statement)를 값으로 표현할 수 있는 표현식(statement) 로 간주합니다.
그러다보니 Java 에서는 불가능했던 if 문을 표현식인 값으로 표현할 수 있습니다.(변수에 할당하거나 바로 리턴할 수 있습니다)
fun isAdult(age: Int): Boolean{
return if(age >=20) true else false
}위와 같은 코드로 if를 표현식으로 바로 리턴 할 수도 있습니다.
그리고 자바의 switch 문과 비슷한 when 을 사용해서 조건 식을 보다 세밀하고 다양하게 표현할 수 있습니다.
when 또한 표현식으로 간주되어 변수에 값을 할당할 수도 있고 바로 리턴을 할 수도 있습니다.
fun isAdult(age: Int): String{
return when (age) {
in 0..10 -> "어린이"
in 11..20 -> "청소년"
else -> "어른"
}
}when 으로 조건을 세분해서 String 으로 리턴 합니다. in 과 .. 을 사용해서 조건이 포함되는지를 표현하고 -> 사용해서 조건에 해당하는 값을 지정 합니다.
대표적인 반복문인 for 언어를 살펴 보겠습니다. Java 에서는 for(int i = 0, i < 10; i++) 로 조건문의 분기값을 지정하는데,
Kotlin 에서는 when 에서 살펴봤던 in 과 .. 을 통해 증감을 표현합니다.
for(i in 1..10) {
println(i)
}숫자가 내려갈 경우에는 .. 이 아닌 downTo 를 사용 합니다.
for(i in 10 downTo 1) {
println(i)
}while 문과 do while문은 거의 유사 합니다.
Java 와 조금 다른 부분인데, Kotlin 에서 함수는 fun 으로 시작 합니다.
fun main() 이름이 main 인 함수 입니다.
만약 함수의 매개변수가 있다면 아래와 같이 매개변수를 ( ) 안에 표현합니다.
fun main(age: Int)
반환값이 있다고 하면 함수 마지막에 : 를 입력하고 반환 타입을 명시합니다.
fun main(age: Int): String String 타입을 리턴 합니다.
함수의 본문은 { } 안에 작성해 주면 되는데요, Java 와 또 다른 점은 하나의 표현식으로 표시되는 함수의 본문은 { }로 표시하지 않고 = 을 사용할 수 있습니다.
이 경우에는 함수의 리턴타입을 추론할 수 있기 때문에 리턴 타입은 생략 가능합니다.
fun max(a: Int, b: Int) = if (a > b) a else b 표현이 하나의 식이기 때문에 = 을 사용했고 결과값이 추론 가능하기 때문에 함수의 반환 타입을 생략 했습니다.
Kotlin 은 Java 와 100% 호환을 목표로 합니다.
그래서, 기존 Java 클래스나 다른 Kotlin 클래스에 어떤 함수나 필드를 추가할 수 있는 기능을 지원하는데, 이를 '확장 함수', '확장 프로퍼티' 라고 합니다.
확장함수는 아래와 같이 확장하고 싶은 클래스(수신객체타입) 와 이 클래스에 속하는 인스턴스 객체(수신객체)를 사용해서 표현할 수 있습니다.
fun String.lastChar(): Char = this.get(this.length - 1) String 클래스를 커스텀하게 확장해서 lastChar 라는 함수를 만들었고,
String 클래스의 인스턴스(this)를 받아서 마지막 단어를 반환하는 함수 입니다.
이 함수를 사용하려면 아래와 같이 사용해주면 됩니다.
"Kotlin".lastChar()
조심할 점은 확장함수는 오버로드되지 않고 항상 수신객체타입의 인스턴스를 반환합니다.
Java 와 동일하게 클래스와 다영성, 상속, 인터페이스, enum 등을 지원 합니다.
표현이 조금 다른 부분이 있는데, 이 부분만 간단히 살펴 보겠습니다.
먼저 클래스는 접근 지시자를 생략하면 기본이 public 입니다. Java 와 조금 다른 부분이니 이 부분은 주의하셔야 될것 같아요.
클래스는 class 키워드를 사용해서 선언 해줍니다. 특이한 점은 생성자 부분인데, 클래스 이름 뒤에 ( ) 를 입력하고 여기에 프로퍼티를 지정해 줍니다.
class Person(
val name: String,
var age: Int)Java 와 조금, 아니 많이 틀립니다. 무엇보다 클래스의 바디 부분이 없어도 클래스 표현이 가능 합니다.
생성자 부분에 어떤 선행작업을 하기 위해서는 init 을 명시해 줘야 합니다.
class Person(
val name: String,
var age: Int
){
init {
if(age < 18){
throw IllegalArgumentException("나이는 18세 이상이어야 합니다")
}
}
}class 명 뒤에 ( ) 를 사용해서 생성자를 지정해 주는데, 이 경우 자동으로 getter 와 setter 도 만들어 줍니다.
위의 Person 클래스의 경우에는 name 은 val 로 선언했기 때문에 재할당이 불가능함으로 Getter 만 생성해주며
age는 재할당이 가능한 var 로 선언했기 때문에 getter 와 setter 를 동시에 생성해 줍니다.
Java 에서는 new 연산자로 클래스의 객체를 생성하는데, Kotlin 에서는 new 없이 사용가능하며 인스턴스 참조도 객체가 아닌 프로퍼티(변수) 그대로 사용 합니다.
fun main() {
val person = Person("Alice", 29)
println(person.age)
}그리고 커스텀 getter 를 지원하는데, 클래스의 바디에 프로퍼티 변수를 선언하고, 이 변수의 getter를 직접 만들어 줄 수 있습니다.
만약 이름을 무조건 대문자로 변경하는 커스텀 getter를 만들경우 아래와 같이 만들어줄 수 있습니다.
class Person(
val name: String,
var age: Int
) {
val isUppercase
get() = name.uppercase()
init {
if (age < 18) {
throw IllegalArgumentException("나이는 18세 이상이어야 합니다")
}
}
}
fun main() {
val person = Person("Alice", 29)
println(person.age)
println(person.isUppercase)
}상속은 Java 와 동일하지만 클래스 명 뒤에 : 를 입력하고 상속할 클래스 이름을 명시해 줍니다.
그리고 부모클래스의 이름 뒤에 생성자를 사용해서 부모 클래스의 생성자(super)를 호출해 줍니다.
상속을 위해서 부모 클래스에 open 을 입력하고 override 하는 프로퍼티에도 open 을 명시해줘야 합니다.
아래 코드는 Animal 과 Animal 을 상속한 Cat 클래스를 간단하게 표시했습니다.
open class Animal(
val species: String,
val legCount: Int
) {
open fun move() {
println("move move")
}
}
class Cat(
species: String,
) : Animal(species, 4) {
override fun move() {
println("고양이가 걸어 갑니다")
}
}
fun main() {
val myCat = Cat("먼치킨")
println(myCat.move())
println(myCat.legCount)
}Java 에서는 Predicate, Consumer, Function, Supplier 를 사용한 함수형 프로그래밍이 가능 합니다.
여기에 -> 를 사용한 Lambda 식으로 더 간결하게 코드를 표시할 있는데 Kotlin 에서는 보다 간결하게 표현이 가능 합니다.
Java 의 Predicate 를 사용한 함수형 프로그래밍의 코드의 예 입니다.
public class Fruit {
private String color;
private Double weight;
public Fruit(String color, Double weight) {
this.color = color;
this.weight = weight;
}
private static List<Fruit> fruitsFilter(List<Fruit> fruits, Predicate<Fruit> filter) {
List<Fruit> filteredFruits = new ArrayList<>();
for (Fruit fruit : fruits) {
if(filter.test(fruit)) {
filteredFruits.add(fruit);
}
}
return filteredFruits;
}
public static void main(String[] args) {
Fruit fruit1 = new Fruit("red", 1.0);
Fruit fruit2 = new Fruit("yellow", 2.0);
Fruit fruit3 = new Fruit("red", 0.5);
List<Fruit> fruits = List.of(fruit1, fruit2, fruit3);
//todo : 컬러가 red 인 과일만 추출
List<Fruit> redFruits = fruitsFilter(fruits, fruit -> fruit.color.equals("red"));
System.out.println("redFruits = " + redFruits);
//todo : 무게가 1.0 이상인 과일만 추출
List<Fruit> heavyFruits = fruitsFilter(fruits, fruit -> fruit.weight > 1.0);
System.out.println("heavyFruits = " + heavyFruits);
}
@Override
public String toString() {
return "Fruit{" +
"color='" + color + '\'' +
", weight=" + weight +
'}';
}
}위 클래스를 Kotlin 으로 변경하면 아래와 같습니다. 클래스앞에 data 를 사용하고, 생성자 프로퍼티를 사용해서 상당히 간결하게 표시가 가능합니다.
또한 Java 코드에서는 함수를 매개변수로 전달할 수 없지만(함수형 인터페이스를 실제로 실행함), Kotlin 에서는 함수자체를 매개 변수로 전달 할 수 있습니다.
fun main() {
val fruits = listOf(
KotlinFruit("red", 1.0),
KotlinFruit("yellow", 2.0),
KotlinFruit("red", 0.5),
)
//todo : 컬러가 red 인 과일만 추출
val redFruits = filterFruits(fruits) { it.color == "red" }
println("redFruits = $redFruits")
//todo : 무게가 1.0 이상인 과일만 추출
val heavyFruits = filterFruits(fruits) { it.weight > 1.0 }
println("heavyFruits = $heavyFruits")
}
data class KotlinFruit(
val color: String,
val weight: Double
)
fun filterFruits(fruits: List<KotlinFruit>, filter: (KotlinFruit) -> Boolean): List<KotlinFruit> {
val filteredFruits = mutableListOf<KotlinFruit>()
for (fruit in fruits) {
if(filter(fruit)) {
filteredFruits.add(fruit)
}
}
return filteredFruits
//todo : 위의 코드는 Kotlin 의 filter() 를 통해 더 간결한 표시가 가능합니다
//return fruits.filter(filter)
}기분 탓인지 모르겠지만, Java 코드보다 간결해 보입니다. Kotlin 에서는 Lambda 함수를 { } 로 감싸서 사용 합니다.
{ fruit -> fruit.color == "red} -> { it.color == "red} 와 같이,
Lambda 함수에서 매개변수가 한개일 경우 it 으로 대체해서 보다 간결하게 표시할 수 있습니다.
또한 함수에서 마지막 파라미터에 람다를 쓸 경우에는 ( ) 오른쪽으로 Lambda 함수를 보낼 수 있습니다.
val redFruits = filterFruits(fruits) { it.color == "red" }
Kotlin 에서는 위에서 말씀드린 것 외에 많은 내용이 있습니다. Java 를 배우신 분은 Kotlin 을 학습하는데 도움이 되실것 같습니다.
저 역시 Kotlin 을 걸음마 단계로 배우고 있으며 짧게나마 공부한 내용을 남겨 봅니다.
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.