map
public class Exam04 {
public Exam04(){
var hashMap = new HashMap<String, Object>();
hashMap.put("key", "value");
hashMap.put("key", 10);
var map = Map.of(
"key1","value1",
"key2","value2",
"key3","value3"
);
hashMap.get("key");
}
}
fun Main() {
// 코틀린에서도 마찬가지로 key value 형식으로 map을 만들 수 있다. -> 기본형태는 immutable
// java의 Object -> 코틀린에서는 Any
// pair라는 문법
val map = mapOf<String, Any>(
Pair("", ""),
"key" to "value"
)
// 만약 mutable로 하려면
val mutableMap = mutableMapOf(
"key" to "value"
)
mutableMap.put("key", "value");
mutableMap.put("key2", "value2");
// 이렇게도 쓸 수 있다.
mutableMap["key"] = "value";
val value = mutableMap["key"];
val hashMap = hashMapOf<String, Any>()
// kotlin에는 triple이라는 자료형태도 있음
var triple = Triple<String, String, String>(
first = "",
second = "",
third = ""
)
}
기본적인 Map 생성
Java
var hashMap = new HashMap<String, Object>();
hashMap.put("key", "value");
hashMap.put("key", 10);
Kotlin
val map = mapOf<String, Any>(
"key" to "value"
)
- Java에서는
HashMap
클래스를 이용해 Map을 생성하고,put
메서드로 키와 값을 추가한다. - Kotlin에서는
mapOf
함수를 이용하여 불변의 Map을 생성할 수 있다. 키와 값은to
키워드를 이용하여 연결한다.
불변과 가변 Map
Java
var map = Map.of(
"key1", "value1",
"key2", "value2",
"key3", "value3"
);
- Java에서는
Map.of
메서드를 이용하여 불변의 Map을 생성할 수 있다.
Kotlin
val mutableMap = mutableMapOf(
"key" to "value"
)
mutableMap.put("key2", "value2");
- Kotlin에서는
mutableMapOf
함수를 이용하여 가변의 Map을 생성할 수 있다. 추가적으로 값을 넣을 때는put
메서드나 대괄호([]
)를 이용할 수 있다.
값 가져오기
Java
hashMap.get("key");
Kotlin
val value = mutableMap["key"];
- Java와 Kotlin 모두 키를 이용하여 값을 가져올 수 있다. 하지만 Kotlin에서는 대괄호를 이용하여 좀 더 간결하게 값을 가져올 수 있다.
Kotlin의 추가적인 기능
- Kotlin에서는
Triple
이라는 자료형도 제공한다. 이는 세 개의 다른 타입의 값을 하나로 묶을 수 있다.
var triple = Triple<String, String, String>(
first = "",
second = "",
third = ""
)
고차함수
이번에는 Java와 Kotlin에서 고차함수와 람다식을 어떻게 다루는지 알아보자.
public class Exam05 {
private Predicate<String> stringPredicate = new Predicate<String>() {
@Override
public boolean test(String s) {
return s.equals("?");
}
};
// 고차함수 -> 함수를 파라미터로 넘기기
public Exam05(){
var strList = List.of(
"1",
"2",
"홍길동",
"ㅇㅇㅇ"
);
// predicate라는 functional interface를 람다식으로
var result = strList.stream().filter(it->{
return it.equals("?");
}).toList();
// 위에서 선언한 stringPredicate 를 넘겨줄 수도 있다.
var result2 = strList.stream().filter(stringPredicate).toList();
// 어차피 메서드가 하나이기 때문에 화살표 함수 -> 람다식으로 표현한 것임
// 코틀린에서는 ?
}
}
fun main() {
var numberList = listOf<Int>(1,2,3,4,5)
numberList.first{it % 2 == 0}
val pred1 = object : Predicate<Int> {
override fun test(t: Int): Boolean{
return t%2 ==0
}
}
numberList.stream().filter(pred1)
// 2가지 변수를 받아서 x+y를 리턴
val add = { x: Int, y:Int -> x+y}
println(add(2,3))
// 익명 메서드를 만들어서 넘겨줄 수 있음 -> 자바에서는 인터페이스가 있어야 하지만
val _add = fun(x:Int, y:Int):Int {
return x+y
}
println(_add(3,4))
lambda(4,5, _add)
// 코틀린에서는 이렇게 메서드를 넘겨주는 방식이 다양하고 간편
// 고차함수, 람다식을 좀 더 편하게 사용
numberList.map{
it->
val t = it
}
}
// 메서드는 Int 두개를 받아서 Int를 넘겨준다
fun lambda(x:Int, y:Int, method:(Int, Int)-> Int){
println(method(x,y))
}
고차함수란?
고차함수는 다른 함수를 인자로 받거나 결과로 반환하는 함수.
Java에서의 고차함수와 람다식
Java 8부터 람다식을 지원한다. Predicate
같은 함수형 인터페이스를 이용하여 람다식을 사용할 수 있다.
var strList = List.of(
"1",
"2",
"홍길동",
"ㅇㅇㅇ"
);
var result = strList.stream().filter(it->{
return it.equals("?");
}).toList();
Kotlin에서의 고차함수와 람다식
Kotlin은 람다식과 고차함수를 아주 간결하게 표현할 수 있다.
val add = { x: Int, y: Int -> x + y }
println(add(2, 3))
val _add = fun(x: Int, y: Int): Int {
return x + y
}
println(_add(3, 4))
fun lambda(x: Int, y: Int, method: (Int, Int) -> Int) {
println(method(x, y))
}
- Kotlin에서는 함수를 다양한 방식으로 넘길 수 있다. 람다식을 이용하거나 익명 함수를 선언해서 넘길 수도 있다.
예제 비교
Java
var result2 = strList.stream().filter(stringPredicate).toList();
- Java에서는
Predicate
라는 함수형 인터페이스를 이용한다.
Kotlin
lambda(4, 5, _add)
- Kotlin에서는 직접 함수를 인자로 넘길 수 있다.
결론
Java와 Kotlin, 둘 다 고차함수와 람다식을 지원하지만, Kotlin이 더 다양한 방식을 제공하며 코드도 간결하다. Kotlin을 이용하면 람다식과 고차함수를 더 효율적이고 간단하게 사용할 수 있다.
Kotlin이 고차함수를 다루는 것을 보면 react, typescript 에서 사용하는 함수형 문법이 떠오르는데, 함수형 표현이 좀 더 자유로워지는 거 같다. 확실히 동적이고 유연한 프로그래밍에 있어서 더 편하고 쉽다는 점에서, 장점이 잘 드러나는 것 같고 그 때문에 함수형 프로그래밍이 점점 더 각광을 받는지도...
클래스 - 상속과 인터페이스
public class Exam06 {
public Exam06(){
var dog = new Dog("퍼피");
dog.eat();
dog.bark();
}
public static void main(String[] args) {
new Exam06();
}
}
@Getter
abstract class Animal implements Bark{
private String name;
public Animal(String name){
this.name =name;
}
public void eat(){
System.out.println("name = " + name);
}
}
class Dog extends Animal{
public Dog(){
super("");
}
public Dog(String name){
super(name);
}
@Override
public void bark() {
System.out.println(this.getName() + "bark");
}
}
interface Bark{
void bark();
}
fun main() {
// new 키워드 없이도 생성
val dog = Dog("해피")
dog.eat()
dog.bark()
}
interface Bark {
fun bark()
}
abstract class Animal (
// 코틀린에서는 생성자 부분이 바로 선언이 됨
private val name: String? = "") : Bark{
// body - >
fun eat() {
println("$name 식사 !")
}
}
// 이렇게만 선언해도 됨
//class Dog()
// Animal을 상속하는데 null을 받아도 되기 때문에 이렇게 호출이 가능함
//class Dog(): Animal()
// 생성자를 받아서 슈퍼라는 키워드로 넘기는 것 표현
class Dog(
private val name:String?=null
) : Animal(name),
// Temp 인터페이스 다중 상속 -> 파라미터 없으므로 그냥 상속
Temp{
override fun bark() {
println("$name : 멍멍")
}
override fun hi() {
println("hi")
}
}
interface Temp{
fun hi()
}
상속
Java
Java에서 상속을 사용할 때에는 extends
키워드를 사용한다. 생성자에서 부모 클래스의 생성자를 호출할 때에는 super
키워드를 사용하며, 이는 반드시 서브 클래스의 생성자 첫 라인에 와야한다.
public class Dog extends Animal {
public Dog(String name){
super(name);
}
}
Kotlin
Kotlin에서는 :
(콜론)을 사용하여 상속을 나타낸다. 여기서 super
키워드를 사용하여 부모 클래스의 생성자를 호출할 수 있다.
class Dog(
name: String? = null
) : Animal(name)
인터페이스
Java
Java에서 인터페이스는 interface
키워드로 선언된다. 구현 클래스에서는 implements
키워드를 사용하여 인터페이스를 구현한다.
public class Dog extends Animal implements Bark {
@Override
public void bark() {
System.out.println(this.getName() + "bark");
}
}
Kotlin
Kotlin에서도 interface
키워드로 인터페이스를 선언한다. 구현은 상속과 동일하게 :
(콜론)을 사용하여 나타낸다.
class Dog(
name: String? = null
) : Animal(name), Bark, Temp
getter와 setter 차이
Java
Java에서는 getter와 setter 메서드를 명시적으로 작성해야 한다.
public class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Kotlin
Kotlin에서는 getter와 setter가 자동으로 생성된다. 이로 인해 코드가 더 간결해진다. 물론, 커스텀 getter나 setter를 정의할 수도 있다.
class Animal(var name: String) {
// ...
}
코틀린에서 캡슐화를 하고 싶다면?
코틀린에서는 접근 제어자를 사용하여 캡슐화를 할 수 있다. 예를 들면,
- private: 해당 요소가 선언된 파일 또는 클래스 내에서만 접근 가능.
- protected: 해당 클래스와 그 하위 클래스에서만 접근 가능.
- internal: 같은 모듈 내에서 접근 가능.
- public: 어디서든 접근 가능. (기본값)
예를 들어, 클래스의 속성을 private로 설정하면 외부에서 직접 접근할 수 없고, getter나 setter 메서드를 통해서만 접근할 수 있다.
class Animal {
private var name: String = "Unknown"
fun setName(name: String) {
this.name = name
}
fun getName(): String {
return this.name
}
}
코틀린에서 사용하는 property에 대해
코틀린에서는 프로퍼티(property)라는 개념이 중요하다. 자바에서는 일반적으로 필드와 이에 대응하는 getter, setter 메서드를 가지지만, 코틀린에서는 이런 것들을 한 번에 다루는 방식을 제공한다.
기본 프로퍼티 선언
기본적인 프로퍼티 선언은 다음과 같다.
var propertyName: Type = initialValue
또는 초기값이 없고, 나중에 할당할 것이라면:
var propertyName: Type
커스텀 getter와 setter
코틀린에서는 프로퍼티에 커스텀 getter와 setter를 쉽게 추가할 수 있다.
var myProperty: Int = 0
get() = field
set(value) {
if (value >= 0) {
field = value
}
}
lateinit과 lazy
lateinit
과 by lazy
두 키워드를 통해 지연 초기화(lazy initialization)를 지원한다.
lateinit
: 나중에 초기화할 것이라는 것을 알리는 키워드. 주로 의존성 주입(DI)에 사용된다.by lazy
: 이 키워드는 프로퍼티가 처음으로 접근될 때 초기화되게 한다.
lateinit var lateInitProperty: String
val lazyProperty: String by lazy { "I'm lazy!" }
Data 클래스
코틀린의 data 클래스는 자동으로 equals()
, hashCode()
, toString()
등을 구현해주며, var
또는 val
로 선언된 프로퍼티에 대해 자동으로 getter와 setter를 제공한다.
data class Person(var name: String, val age: Int)
코틀린의 프로퍼티 기능은 코드를 훨씬 간결하고 안전하게 만들어준다.
reference:
- fastcampus 시그니처 백엔드 path 초격차 패키지 course 5
- https://kotlinlang.org/spec/introduction.html#reference
'Programming > Kotlin' 카테고리의 다른 글
java에는 없고 kotlin에만 있는 함수 : let, also, apply, run (1) | 2023.10.22 |
---|---|
Java와 Kotlin 비교: default value, when, 확장함수 (1) | 2023.10.22 |
Java와 Kotlin 비교 : 변수, null 안정성, 엘비스 연산자, 가변/불변 컬렉션 (1) | 2023.10.22 |