
이번 포스팅에서는 예외 처리(try-catch-finally), Checked vs Unchecked Exception, try-with-resources & use 함수, 코틀린의 Default Parameter & Named Argument, 가변인자, 클래스 초기화, 커스텀 getter, 상속과 인터페이스 구현 그리고 접근제어 키워드 등을 정리해 보겠습니다. 자바와 코틀린에서 어떻게 다른지 비교해가며 살펴보도록 하겠습니다.
1. try-catch-finally
자바와 코틀린 모두 예외 처리를 위해 try-catch-finally 블록을 사용합니다.
다만, 코틀린에서는 try-catch가 “식(Expression)”이므로 아래처럼 값을 반환할 수 있습니다.
Java
try-catch-finally는 “문(Statement)”이므로 직접 반환값을 형성하기 어렵습니다.
private int parseIntOrThrow(String str) {
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("잘못된 숫자입니다.");
} finally {
System.out.println("finally는 무조건 실행됩니다.");
}
}
Kotlin
try-catch가 식(Expression)이기 때문에 블록 전체를 통으로 반환값으로 활용 가능합니다.
fun parseIntOrThrow(str: String): Int {
return try {
str.toInt()
} catch (e: NumberFormatException) {
throw IllegalArgumentException("잘못된 숫자입니다.")
} finally {
println("finally 블록은 항상 실행됩니다.")
}
}
2. Checked Exception vs Unchecked Exception
Java
- Checked Exception: 반드시 예외 처리를 강제하는 예외(IOException 등)
- Unchecked Exception: 런타임 시점에 발생하며, 강제 예외 처리가 없는 예외(RuntimeException 등)
public void readFile() throws IOException {
BufferedReader br = new BufferedReader(new FileReader("test.txt"));
// ...
}
Kotlin
- 코틀린에는 Checked Exception 개념이 없습니다. 즉, IOException 등도 명시적 예외 처리가 강제되지 않습니다.
- 예외를 꼭 처리하지 않아도 컴파일 에러가 발생하지 않습니다.
fun readFile() {
val reader = BufferedReader(FileReader("test.txt"))
// ...
}
3. try-with-resources vs use
Java
JDK 7부터 추가된 try-with-resources 구문을 사용하면 자동으로 close()가 호출됩니다.
public void readFile2(String path) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
System.out.println(reader.readLine());
}
}
Kotlin
코틀린에는 try-with-resources가 없습니다. 대신 use라는 inline 확장 함수를 제공합니다. 이건 익숙하지가 않아서 가독성이 안좋아보이는데, 이번 프로젝트에서 사용하면서 눈에 익혀보겠습니다.
fun readFile2(path: String) {
BufferedReader(FileReader(path)).use { reader ->
println(reader.readLine())
}
}
4. Default Parameter와 Named Argument
Java
Default Parameter가 없으므로, 오버로딩 메서드를 여러 개 만들어야 합니다.
public void repeat(String str, int num, boolean useNewLine) {
// ...
}
public void repeat(String str, int num) {
repeat(str, num, true);
}
public void repeat(String str) {
repeat(str, 3);
}
Kotlin
파라미터에 기본값을 지정할 수 있고, Named Argument를 통해 원하는 파라미터만 선택하여 값 설정도 가능합니다. 빌더 패턴 + JS의 Default Value가 섞인 느낌이라 정말 좋네요
fun repeat(
str: String,
num: Int = 3,
useNewLine: Boolean = true
) {
// ...
}
// 사용 예시
repeat("Hello")
repeat("Hello", 5)
repeat("Hello", useNewLine = false)
5. 가변인자 (Varargs)
Java
public static void printAll(String... strings) {
for (String str : strings) {
System.out.println(str);
}
}
Kotlin
코틀린에서는 vararg 키워드를 사용합니다. 배열을 전달하려면 스프레드 연산자(*)를 붙입니다. JS에서도 ES6에 스프레드 연산자가 있어서 사용법만 상기해두면 좋아 보입니다.
fun printAll(vararg strings: String) {
for (str in strings) {
println(str)
}
}
val array = arrayOf("A", "B")
printAll("Hello", "Kotlin")
printAll(*array) // 스프레드 연산자
6. 클래스 선언과 초기화 블록
Java
생성자 + 필드 + getter/setter를 명시적으로 작성해야 합니다.
public class JavaPerson {
private final String name;
private int age;
public JavaPerson(String name, int age) {
if (age <= 0) throw new IllegalArgumentException("나이가 잘못됨");
this.name = name;
this.age = age;
}
// getter/setter ...
}
Kotlin
- 생성자와 필드 선언을 동시에! 자바 17에 생긴 record 타입과 비슷하네요.
- init 블록으로 초기화 시 검증 로직을 넣을 수 있습니다.
class Person(
val name: String,
var age: Int
) {
init {
if (age <= 0) {
throw IllegalArgumentException("나이가 잘못됨")
}
}
}
- val은 불변 필드(getter만), var는 가변 필드(getter+setter)
7. 커스텀 Getter
코틀린은 프로퍼티 기반으로 Getter/Setter를 작성할 수 있어, 마치 필드처럼 접근 가능합니다.
class Person(val name: String) {
val uppercaseName: String
get() = name.uppercase()
}
val person = Person("kotlin")
println(person.uppercaseName) // "KOTLIN"
8. 상속과 인터페이스 구현
자바
public abstract class JavaAnimal {
protected final String species;
public JavaAnimal(String species) {
this.species = species;
}
public abstract void move();
}
public class JavaCat extends JavaAnimal {
public JavaCat(String species) {
super(species);
}
@Override
public void move() {
System.out.println("사뿐사뿐");
}
}
코틀린
클래스나 인터페이스 구현시에 “:” 기호 사용, 상위 클래스 생성자를 바로 호출해야 합니다.
추상 클래스의 함수나 프로퍼티를 재정의할 때는 override 키워드가 필수입니다.
abstract class Animal(
protected val species: String
) {
abstract fun move()
}
class Cat(species: String) : Animal(species) {
override fun move() {
println("사뿐사뿐")
}
}
9. open 키워드와 초기화 블록 주의
코틀린에서 일반 클래스나 멤버를 상속/오버라이드하려면 open 키워드를 붙여야 합니다.
단, 상위 클래스 생성자 또는 초기화 블록에서 open 프로퍼티를 사용하면, 하위 클래스에서 override된 값이 제대로 초기화되기 전에 호출될 수 있으므로 주의가 필요합니다.
open class Base(open val number: Int = 100) {
init {
println("Base init: $number")
}
}
class Derived(override val number: Int) : Base(number) {
init {
println("Derived init: $number")
}
}
// 생성 순서상 Base init이 먼저 호출되므로, 의도치 않은 값(0)이 찍힐 수 있음
10. 접근 제어자 (Java vs Kotlin)
- Java: 기본 접근제어자가 package-private
- Kotlin: 기본 접근제어자가 public
이외에 private, protected, internal 등이 존재
코틀린에서는 유틸성 함수를 만들 때, 별도의 클래스 없이 파일 최상단 범위에 함수를 배치하는 것이 흔합니다.
// StringUtils.kt
fun isDirectoryPath(path: String) = path.endsWith("/")
- 굳이 유틸 클래스를 만들지 않아도 됩니다.
✏️ 마무리 정리
오늘은 자바와 코틀린에서 예외 처리부터 클래스 상속/인터페이스, 그리고 접근 제어자까지 주요 차이점들을 정리했습니다.
- try-catch-finally와 Checked vs Unchecked Exception
- 자바의 try-with-resources vs 코틀린의 use 함수
- 코틀린의 Default Parameter & Named Argument로 오버로딩 훨씬 줄이기
- 가변 인자(vararg) 처리 및 스프레드 연산자
- 클래스 선언 시 생성자 + 필드를 동시에 작성 가능, init 블록 주의사항
- 커스텀 getter와 프로퍼티 override
- 상속 시에 open 키워드 사용, 인터페이스 구현에서도 “:” 사용
- 접근 제어자 기본값과, 코틀린의 파일 최상단 함수 활용
다음에도 코틀린을 학습하면서 자주 헷갈리는 내용을 정리해보겠습니다.
읽어주셔서 감사합니다!
'Languege > Kotlin' 카테고리의 다른 글
[Kotlin Basic] 기초 문법 학습 5 (0) | 2025.03.12 |
---|---|
[Kotlin Basic] 기초 문법 학습 4 (0) | 2025.03.10 |
[Kotlin Basic] 기초 문법 학습 2 (0) | 2025.03.04 |
[Kotlin Basic] 기초 문법 학습 1 (0) | 2025.03.03 |