Kotlin - 17. Java와 사용하기

2021/12/08
language

코틀린은 자바 jvm 에서 실행 할 수 있기 때문에 당연히 자바로 작성된 코드와 병행하여 사용이 가능합니다.

컴파일

두 코드를 같이 사용하는 방법은 크게 두가지 입니다.

  • 기존에 작성된 자바코드를 jar로 패키징하여 kotlin 실행 클래스패스에 추가
  • maven, gradle 같은 빌드 툴을 통해 하나의 통합 프로젝트에서 작업

대부분의 경우 빌드툴을 이용한 방법을 통해 사용하게 됩니다.

예제

gradle 빌드툴을 사용하여 빌드하는 예제입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

plugins {
id 'org.jetbrains.kotlin.jvm' version '1.6.0'
id 'application'
}

group = 'me.micky'
version = '1.0-SNAPSHOT'

repositories {
mavenCentral()
}

dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
}

test {
useJUnitPlatform()
}

compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}

compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}

1
2
3
4
// Constants.kt
package test

const val value = 100
1
2
3
4
5
6
7
8
package test;

public class Utils {

public int test(int i) {
return ConstantsKt.value * i;
}
}

코틀린 탑레벨에 정의된 상수 값을 자바에서 사용하는 예제입니다. 코틀린에 정의된 상수를 자바에서 사용할 때 바로 변수를 사용하는것이 아닌 파일이름이 변형된 클래스 내부의 상수를 접근하는 형태로 사용하게 됩니다. 따라서 Constants.kt의 이름은 ConstantsKt 라는 네임스페이스로 변경되는 것으로 생각하시면 됩니다.

코틀린에서 자바 호출

코틀린에서 자바 코드를 호출하는 것은 큰 어려움이 없습니다. 코틀린 문법 자체가 자바보다는 넓기 때문에, 크게 문제가 될 부분은 없습니다. 또한 일부 자바의 문법이 코틀린에서 더 간략하게 표시가 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package test;

public class Test {
private int val1;

public int getVal1() {
return val1;
}

public void setVal1(int val1) {
this.val1 = val1;
}


public Test copy() {
Test test = new Test();
test.val1 = this.val1;
return test;
}

public void when() {
System.out.println("when");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package test

object TestUser {

fun test() {
val t = Test()
t.val1 = 10 // setVal1

print(t.val1) // getVal1
}

fun copyTest(t: Test) = t.copy()

fun useWhen() = Test().`when`()
}

Test라는 자바 클래스에는 setter/getter, copy 메소드가 있습니다. 코틀린에서는 getter/setter 는 프로퍼티로 인식하기 때문에, 위 예제 처럼 별도의 함수 호출이 아닌 프로퍼티를 사용하는 형태로 사용됩니다. 그 외의 일반 메소드의 경우 그냥 메소드를 호출하는 일반적인 형태를 가집니다. 그리고 코틀린이 미리 등록된 지시어의 경우에는 `로 감싸서 사용합니다.

자바에서 코틀린 호출

1
2
3
class Struct(val i: Int) {
operator fun plus(s: Struct) = Struct(s.i + this.i)
}
1
2
3
public int structTest(Struct s1, Struct s2) {
return s1.plus(s2).getI();
}

이전에 언급한대로 코틀린의 필드는 기본적으로 프로퍼티이기 때문에 자바에서는 이를 getter/setter로 인식하여 접근하게 됩니다.
그리고 연산자 오버로딩을 통해 작성된 메소드의 경우 이와 매치된 이름을 통해 호출 할 수 있게 됩니다.

companion object & object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Struct(val i: Int) {

companion object {
const val THOUSAND = 1_000

fun test() {
println("test")
}

@JvmStatic
fun staticTest() {
println("static test")
}
}

operator fun plus(s: Struct) = Struct(s.i + this.i)
}
1
2
3
4
5
6
7
@Test
public void t1() {
assert Struct.THOUSAND == 1000;

Struct.Companion.test();
Struct.staticTest();
}

클래스 내부에 선언된 companion object 에 선언된 상수, 메소드의 경우 위 예제와 같이 사용이 가능합니다. 기본적으로 companion object 는 object 이기 때문에 내부에 선언된 메소드를 사용하기 위해선 해당 Companion 레퍼런스를 통해 메소드에 접근해야 하지만 @JvmStatic 어노테이션을 선언한 메소드의 경우 자바에서는 static 메소드로 인식할수 있게 됩니다. 따라서 클래스 이름을 통해 접근할 수 있게 됩니다. 또한 const val 로 선언된 상수의 경우도 클래스 이름을 통해 접근가능합니다.

람다

람다식은 자바에 사용하는 람다식 그대로를 사용합니다. 다만 매칭되는 인터페이스는 코틀린의 것을 따릅니다.

(Int) -> Int 형태의 람다의 경우 코틀린의 Function1<Int, Int> 이라는 인터페이스에 매칭됩니다. 이는 자바에 정의된 Function 과는 다른 인터페이스로 코틀린에 자체적으로 선언된 인터페이스 입니다.

1
2
3
fun test(f: (Int) -> Int) {
println(f(10))
}
1
test(i -> i + 10)

파라메터 기본값 사용

1
2
3
4
@JvmOverloads
fun println(i: Int = 10) {
println(i)
}
1
2
3
4
public void test() {
Test.println();
Test.println(20);
}

파라메터 기본값이 있을 경우 자바에서 사용할 때는 기본값이 없는 형태만 사용할 수 있습니다. 따라서 @JvmOverloads를 이용하여 컴파일 과정에서 오버로딩된 메소드를 추가하고 자바에서는 이를 사용할 수 있습니다.

탑레벨 함수 접근

파일 레벨에 정의된 탑레벨 함수의 경우 처음 예제 처럼 파일이름Kt 라는 네임스페이스로 묶어 접근할 수 있습니다. 이름이 마음에 안드는 경우 어노테이션을 이용해 수정할 수 있습니다.

1
2
3
4
5
6
7
8
@file:JvmName("Util")
package test
// test.UtilFunctions.kt

fun test() {
println(10)
}

1
2
3
public void t2() {
Util.test();
}

어노테이션을 이용해 위 자바 코드 처럼 사용이 가능합니다.

예외 처리

코틀린은 기본적으로 예외를 명시적으로 처리하지 않는 unchecked exception가 기본입니다. 하지만 자바에서는 필요에 따라 checked exception를 사용해야하는 경우가 있습니다.

그런 경우엔 @Throw 어노테이션을 통해 선언가능합니다.

1
2
3
4
@Throws(java.io.IOException::class)
fun readline() {
// some code
}
1
2
3
4
5
6
7
public void test() {
try {
readline();
} catch (IOException e) {
e.printStackTrace();
}
}

기타 어노테이션

그 외 자바에 있지만 코틀린에서는 지원하지 않는 키워드는 매치되는 어노테이션을 이용해 표현이 가능합니다.

  • synchronized -> @Synchronized
  • default(interface) -> @JvmDefault
  • volatile -> @Volatile
  • transient -> @Transient

기타 다른 어노테이션은 여기를 통해 확인할 수 있습니다.


출처

  • 다재다능 코틀린 프로그래밍
  • 코틀린 공식 문서