본문 바로가기
내 것 만들기

Java vs Kotlin 총정리 (for migration)

by nongsusanmul 2024. 8. 30.
반응형

지속적으로 업데이트 예정입니다. 틀린 부분 정정해주시는 댓글은 환영입니다.

 

[ 타입 ]

1. Entry Point

  Java Kotlin
Entry Point public static void main(String[] args) { … } fun main(args: Array<String>) { ... }

 

2. 타입

  Java Kotlin
정수형 byte (1바이트, -128 ~ 127)
short
 (2바이트, -32,768 ~ 32,767)
int
(4바이트, -2,147,483,648 ~ 2,147,483,647)
long
(8바이트, -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807)
Byte
Short
Int
Long
범위는 Java와 같음
실수형 float (4바이트, (3.4 X 10^-38) ~ (3.4 X 10^38))
double
(8바이트, (1.7 X 10^-308) ~ (1.7 X 10^308))
부동소수점은 IEEE-754를 따름
float은 소수점 아래 7자리까지 표현
Float (4바이트, (+/-) 1.4 x 10^-45 ~ (+/-) 3.4028235 x 10^38)
Double
(8바이트, (+/-) 4.9 x10^-324 ~ (+/-) 1.7976931348623157 x 10^308)
부동소수점은 IEEE-754를 따름
float은 소수점 아래 7자리까지 표현
문자형 char (작은 따옴표, 2바이트, ASCII나 유니코드 가능)
String
(큰 따옴표, Reference Type)
Char
String
($템플릿, """템플릿 지원)
그 외 boolean (1바이트)
void, Void
Object
Boolean (1바이트)
Unit
Any
Unsigned , java 8 부터 Integer.parseUnsignedInt -> Integer.toUnsignedString 으로 문자열 형태의 값을 얻을 수 있음 ○, 접미사 U로 표현 가능
Wrapper Class
Auto Boxing
auto boxing auto boxing
접미사 float(F), long(L)
가독성을 위해 대문자로 표기하는 것을 권장

정수 자료형의 범위 밖인 경우, 표기하지 않으면 컴파일 에러
ex) long a = 3100100100; -> Compile Error
실수형은 항상 표기
Float(F), Long(L)
가독성을 위해 대문자로 표기하는 것을 권장

항상 표기
언더스코어 , java 7 부터 지원  (ex. val a = 1_000_000)
초기값 할당 자동
ex) int a; // 0
ex) char b; // '\u0000'
ex) boolean c; // false
수동
초기화 하지않은 변수 사용 시 컴파일 에러
Compile Error : Variable 'a' must be initialized

Auto boxing

 

3. 변수 생성 규칙

  Java Kotlin
기본 규칙 라인 끝 세미콜론 O
여러 라인을 한 줄로 작성할 경우 O
네이밍은 Camel case
네이밍 시작 소문자, 명사형
네이밍 시작 특수문자 사용 불가

네이밍은 클린코드(용도가 명확한 표현) 지향
라인 끝 세미콜론 X
여러 라인을 한 줄로 작성할 경우 O
네이밍은 Camel case
네이밍 시작 소문자, 명사형
네이밍 시작 _(언더 바) 사용 가능, object, top-level을 제외한 클래스 내에 private 변수 생성 시에만 권장
네이밍은 클린코드(용도가 명확한 표현) 지향
일반 변수 {타입} {이름} = {값}; {가변성} {이름}[: 타입] = {값}
객체형 변수 {타입} {이름} = new {생성자호출}; {가변성} {이름}[: 타입] = {생성자호출}
접근 제어자 private을 제외한 접근 제어자 표시 public을 제외한 접근 제어자 표시
상수 final const
전역 static (키워드 순서는 static -> final 권장)
네이밍은 대문자, 단어 사이 언더스코어
top-level, object class, companion object
네이밍은 대문자, 단어 사이 언더스코어

top-level ×
확장 변수 ×
변수 은닉화 getter, setter 함수 정의 대체할 getter 변수 get() =, 필요 시 setter 정의
Nullable @NonNull로 표기하지 않는 이상 항상 nullable 엘비스 연산자
lambda java.util.function의 함수형 인터페이스를 사용하거나 직접 정의하여 이용 val some = (Int) -> String = { it.toString() }
같은 코드 예제 public class Something {
    public int number = 0; // public, 가변 멤버
    public static final long NUMBER_MIN = -100;

    static final long NUMBER_MAX = 100; // private, 전역 불변

    SomeType type = new SomeType(); // 은닉화 대상

    public SomeType getType() {
        return type;
    }

    public void setType(SomeType type) {
        this.type = type;
    }
}
const val NUMBER_MIN = -100L // top-level, 전역 불변

class
Something {
    var number = 0 // public, 가변 멤버

    private var _type = SomeType() // 은닉화 대상
    val type get() = _type

    fun setType(type: SomeType) {
        _type = type
    }

    companion object {
        private const valNUMBER_MAX = 100L// private, 전역 불변
    }
}

 

4. 함수 생성 규칙

  Java Kotlin
기본 규칙 네이밍은 Camel case
네이밍 시작 소문자, 동사형
네이밍 시작 특수문자 사용 불가
네이밍은 클린코드(용도가 명확한 표현) 지향
네이밍은 Camel case
네이밍 시작 소문자, 동사형
네이밍 시작 특수문자 사용 불가
네이밍은 클린코드(용도가 명확한 표현) 지향
일반 함수 {반환타입} {이름}(...) { ... } fun {이름}(...): {반환타입} { ... }
parameters {타입} {이름}, {타입} {이름}, ... {이름}: {타입}, {이름}: {타입}, ...
default value ×, 메서드 오버로드 이용 , ex) fun foo(p1: Int = 0, p2: String = "")
return return {반환값}; return {반환값}
Single Expression 가능 (= {반환값})
접근 제어자 private을 제외한 접근 제어자 표시 public을 제외한 접근 제어자 표시
전역 static top-level, object class, companion object
top-level ×
확장 함수 ×
trailing comma × △, 1.4.0부터 지원
Named Argument ×
getter setter 보일러 플레이트, Lombok으로 코드 최적화 가능 get(), set(value) 제공
lambda java.util.function의 함수형 인터페이스를 사용하거나 직접 정의하여 파라미터로 전달 fun getSome(block: (Int) -> String) {
    // ...
}
같은 코드 예제 public class Something {
    // ...

    public String hello(boolean a) {
        return type.hello(a);
    }
    public static String bye() {
        return type.bye();
    }

}
class Something {
    // ...

    fun hello(a: Boolean): String = type.hello(a)

}

fun Something.bye(): String = type.bye() // top-level, 확장 함수

 

5. 클래스 규칙

  Java Kotlin
기본 규칙 네이밍은 Camel Case
네이밍 시작 대문자, 명사형
네이밍 시작 특수문자 사용 불가
모든 변수 및 함수는 클래스 하위에만 정의 가능
네이밍은 Camel Case
네이밍 시작 대문자, 명사형
네이밍 시작 특수문자 사용 불가
생성자 기본 : 파라미터가 없는 기본 생성자 자동 정의
파라미터를 갖는 생성자 오버로드 시, 기본 생성자도 작성해야 함
기본 생성자 정의 불필요
1종류의 생성자는 클래스 이름 다음에 정의 가능
추가 오버로드 시, constructor 키워드를 이용해 클래스 내부에 정의
init block ×
클래스 종류 class
abstract class
enum
class
abstract class
open class
data class
sealed class
enum class
data class × 타입 관리에 효과적인 클래스
hashcode(), equals(), toString(), copy() 메소드를 자동으로 생성해줌
모두 override가 가능함
생성자 arguments는 불변성(val) 변수를 정의할 것을 권장함
상속 가능 여부 extends 키워드 이용
하나의 supertype class만 상속할 수 있음

class, abstract class 모두 상속 가능
abstract 키워드가 있는 메소드는 필수적으로 @Override
변수는 @Override할 수 없음
abstract 메소드 초기 정의 불가
키워드가 없는 변수 및 함수는 재정의 가능, 외부 접근 시 우선순위는 말단 자식이 가장 높음
:(콜론) 키워드 이용
하나의 supertype class만 상속할 수 있음

open class, abstract class, sealed class 상속 가능
abstract 키워드가 있는 변수 및 함수는 필수적으로 override
abstract
변수 및 함수 초기 정의 불가
open
키워드가 있는 변수 및 함수는 선택적으로 override 및 재정의 가능
open 변수 및 함수 초기 정의 가능
open classabstract 키워드 사용 불가
sealed classabstract와 본질적으로 유사함, enum class의 성질을 추가로 가짐
그러므로 자식 타입이 여러 개인 경우, sealed class를 쓰는 것이 좋음(when)
대신 컴파일 시간을 좀 더 잡아먹음
Singleton Dagger/Hilt 이용하는 경우 @Singleton
직접 구현
Dagger/Hilt 이용하는 경우 @Singleton
object class
같은 코드 예제 enum SomeType {
    BAD, SO_SO, GOOD; // 상수 네이밍 룰
}

abstract class Everything {
    String data = "1";

    abstract String getData(); // 변수는 abstract로 선언 불가
}

class Something extends Everything {
    String data = "2";

    @Override
    String getData() {
        return data + " on Something"; // 2 on Anything
    }
}
enum class SomeType {
    BAD, SO_SO, GOOD

}

sealed class Everything {
    abstract val data: String

    open fun getData(): String {
        return data
    }
}

data class Something(
    override val data: String,
): Everything() {

    override fun getData(): String {
        return "$data on Something"
    }
}

 

 

 

[ Type operation ]

1. Type check

  Java Kotlin
비교 instanceof 이용
Java에서는 OOP가 매우 중요함. 디버깅 과정이 아닌 이상 이러한 코드 유형을 작성할 일은 거의 없어야 한다.
is, !is 이용
when으로도 체크 가능
주로 enum, sealed class와 함께 이용, null이어서 타입 체크가 불가능한 경우에는 false 반환
같은 코드 예제 if (obj instanceof SomeType.GOOD) {
    Log.d("", "success casting to good");
} else if (obj instanceof SomeType.SO_SO) {
    Log.e("", "failure casting (so so)");
} else {
    Log.e("", "failure casting (bad)");
    Log.e("", "or (null)");

}

또는

if (!(obj instanceof SomeType.GOOD)) {
    Log.e("", "failure casting to good");

}
when (obj) { // obj is nullable data
    SomeType.BAD -> Log.e("", "failure casting (bad)")

    SomeType.SO_SO -> Log.e("", "failure casting (so so)")
    SomeType.GOOD -> Log.d("", "success casting to good")
    else -> Log.e("", "failure casting (null)")
}

또는

if (obj !is SomeType.GOOD) {
    Log.e("", "failure casting to good")
}

 

2. Type cast

  Java Kotlin
비교 ({타입}) {이름} 형태로 이용
특정한 데이터의 타입 변경 시나리오에서는 파싱 메소드를 제공 (ex. Integer.parseInt("300"))
객체의 명시적 타입이 캐스팅할 타입보다 상위 일 때 컴파일 경고
객체의 명시적 타입이 캐스팅할 타입과 자손 관계가 아닐 때 컴파일 에러
캐스팅 할 객체가 암시적 타입으로 선언된 경우, 캐스팅 타입보다 상위거나 자손 관계가 아닐 때 ClassCastException 발생
as, as? 이용
캐스팅이 불확실한 경우에는 AType as? BType의 형태로 캐스팅해야 함
AType as BType? 형태에서 캐스팅이 실패할 경우 ClassCastException 발생
같은 코드 예제 Parent parent = new Parent();
Child1 first = (Child1) parent; // 컴파일 경고

Child2 second = new Child2();
Child1 first = (Child1) second; // 컴파일 에러

Parent first = new Child1();
Child2 second = (Child2) fisrt; // ClassCastException
val parent = Parent()
val first = parent as Child1 // 컴파일 경고, ClassCastException

val second = Child2()
val first = second as Child1 // 컴파일 경고, ClassCastException

val first: Parent = Child1()
val second = first as Child2 // 컴파일 경고, ClassCastException

 

 

 

[ Collections ]

1. Array

  Java Kotlin
비교 중괄호로 표현 가능, Premitive Type
초기 크기 지정 가능, Reference Type
int a[ ] 와 같은 형태로도 선언이 가능하지만, 가독성을 위한 컨벤션 트렌드는 타입 뒤에 대괄호를 붙이는 형태임
(여러 IDE 사이트의 기본 main 문에는 String[] args의 형태로 작성되어 있기도 함)
(해당 컨벤션은 성능 차이도 없음)

기본적으로 추가/삭제 불가, 수정 가능
메모리 재할당이나 ArrayList를 이용해 추가/삭제 구현 가능, 자주 변경되는 데이터라면 ArrayList 이용
Array 객체 이용
Premitive Type에 매칭되는 Array 객체 이용 가능
(IntArray, BooleanArray, CharArray 등)
모든 Array는 arrayOf의 형태로 초기 선언 가능
초기 크기 지정 가능, 초기값 람다 연산 가능

추가/수정 가능
Array를 가변 변수(var)로 선언한 후, plus() 혹은 + 연산자 이용하여 추가 가능한 방식
filter를 이용하거나 MutableList로 변환하여 삭제 구현 가능
같은 코드 예제 int[ ] a = { 1, 3, 5, 7, 9 };
int[ ] b = new int[5]; // null 초기화
int[ ] c = new int[5] { 1, 3, 5, 7, 9 };

// 추가
int[] newArr = new int[a.length + 1];
System.arraycopy(a, 0, newArr, 0, a.length);
newArr[a.length] = 11;
a = newArr; // 1, 3, 5, 7, 9, 11
// 수정
a[3] = 19 // 1, 3, 5, 19, 9, 11
// 삭제
int[] newArr = new int[arr.length - 1];
System.arraycopy(a, 0, newArr, 0, a.length - 1);
a = newArr;
 // 1, 3, 5, 19, 9

var a = intArrayOf(1, 3, 5, 7, 9)
var b = Array<Int>(5) { null }
var c = IntArray(5) { it * 2 + 1 } // it is index

// 추가
a += 11 // 1, 3, 5, 7, 9, 11
// 수정
a[3] = 19 // 1, 3, 5, 19, 9, 11
// 삭제
a = a.filter { it != 5 }.toIntArray() // 1, 3, 19, 9, 11
a = a.toMutableList().apply {
    removeLast()
}.toIntArray() // 1, 3, 19, 9

 

2. List

  Java Kotlin
비교 List는 abstract class
List를 구현한 여러 Collection을 선언하여 이용
ArrayList, LinkedList
추가/수정/삭제 모두 가능
불변성의 경우 List 이용
가변성의 경우 MutableList 이용
listOf(), listOf<>()
mutableListOf(), mutableListOf<>()
List는 emptyList(), emptyList<>()도 가능
위의 형태로 주로 초기화함
모든 Collection은 확장함수 toList()로 캐스팅 가능
Map을 제외한 모든 Collection은 확장함수 toMutableList()로 캐스팅 가능
성능   0크기 - EmptyList
1크기 - SingletonList
2크기 이상 - ArrayList
MutableList === ArrayList
타입을 잘게 쪼개어 놨다. 내부적으로 메모리 관리의 효율성 때문이 아닐까 싶다.
같은 코드 예제 List<Integer> arrayList = new ArrayList<>(10); // param '10' is not array length
List<Integer> linkedList = new LinkedList<>(arrayList);

arrayList.set(3, 19); // IndexOutOfBoundsException
arrayList.set(9, 20); // IndexOutOfBoundsException

arrayList.add(5);
linkedList.add(8);
val list = List<Int>(5) { it * 2 + 1 }
val empty = emptyList<Int>() // length = 0

val mutable: MutableList<Int> = mutableListOf()
var mutable2 = mutableListOf<Int>() // Bad, duplicated mutable

list.add(19) // Compile error
mutable.add(19) // Good

val newList = list.toMutableList()
newList.add(19) // Good

 

3. Set

  Java Kotlin
비교 Set은 abstract class
Set을 구현한 여러 Collection을 선언하여 이용
ArraySet, HashSet, TreeSet, LinkedHashSet
추가/삭제 가능, 순서가 없기 때문에 수정 불가
불변성의 경우 Set 이용
가변성의 경우 MutableSet 이용
setOf(), setOf<>()
mutableSetOf(), mutableSetOf<>()
Set은 emptySet(), emptySet<>()도 가능
위의 형태로 주로 초기화함
Map을 제외한 모든 Collection은 확장함수 toSet(), toMutableSet()으로 캐스팅 가능

성능   기본적으로 HashSet의 구조
contains 성능이 O(1)
같은 코드 예제 Set<Integer> hashSet = new HashSet<>();

hashSet.add(3);
hashSet.remove(3);
val set = mutableSetOf<Int>()

set.add(3);
set.remove(3);

 

4. Map

  Java Kotlin
비교 Map은 abstract class
Map을 구현한 여러 Collection을 선언하여 이용
ArrayMap, HashMap, TreeMap, LinkedHashMap 
추가/수정/삭제 모두 가능
삭제 시 key 하나만 넣거나, key-value를 모두 넣어 boolean 값을 반환받을 수 있음
불변성의 경우 Map 이용
가변성의 경우 MutableMap 이용
mapOf(), mapOf<>()
mutableMapOf(), mutableMapOf<>()
Map은 emptyMap(), emptyMap<>()도 가능
위의 형태로 주로 초기화함
모든 Collection은 확장함수 associateBy()으로 캐스팅 가능
성능   기본적으로 HashMap의 구조
containsKey 성능이 O(1)
같은 코드 예제 Map<Integer, Integer> hashMap = new HashMap<>();

hashMap.put(0, 19);
hashMap.put(3, 20);
int a = hashMap.get(5); // unboxing fail warning, because may return null
hashMap.remove(4); // do nothing
val map = mutableMapOf<Int, Int>()

map[0] = 19
map[3] = 20
val a = map[5] // null
map.remove(4) // do nothing

 

반응형