좀 더 호출하기 쉬운 함수 작성
먼저 만들어진 Collections 객체를 출력해보도록 하자.
Java의 Collections를 사용하기 때문에 기본적인 toString()
을 사용할 순 있지만, 출력 형식이 고정되어 있다.
참고 AbstractCollection#toString
Returns a string representation of this collection.
The string representation consists of a list of the collection’s elements in the order they are returned by its iterator, enclosed in square brackets (“[]”).
Adjacent elements are separated by the characters “, “ (comma and space). Elements are converted to strings as by String.valueOf(Object).
1 | >>> val list = listOf(1, 2, 3) |
위의 출력 결과를 [1, 2, 3]
에서 (1; 2; 3)
으로 교체한다고 가정해보자.
Java에서 위의 문제를 해결하기 위핸 별도의 라이브러리를 사용하거나 기존 Collections의 로직을 새로 작성해야 한다.
코틀린은 이러한 작업을 표준 라이브러리에서 처리할 수 있도록 기본 제공해주고 있다.
이번 포스팅에서 jsonToString
이라는 함수를 구현해나가면서 위의 문제를 해결해보도록 하자.
1 | fun <T> joinToString( |
위의 코드에 작성된 jsonToString
함수는 파라미터로 넘겨받은 Collection의 요소들을 뽑아내어, StringBuilder
사이에 구분 기호 및 접두사와 접미사를 추가하여 원하는 출력 포맷을 만들 수 있도록 처리해준다.
이 코드를 이용해 아래와 같이 실행한다면 문제를 해결할 수 있을 것이다.
1 | >>> val list = listOf(1, 2, 3) |
위의 로직은 정상적으로 동작하지만, 호출할 때마다 4개의 파라미터를 넘겨줘야한다.
이를 좀 더 단순하게 만들어보도록 하자.
이름이 지정된 가변인자
첫 번재 문제는 각 파라미터가 어떤 값을 받을 것인지 알 수 없기때문에 가독성이 떨어진다는 것이다.
1 | joinToString(collection, " ", " ", ".") |
2~4번째 파라미터의 경우 어떤 문자열이든 넘길 수 있을까?
만약 넘길 수 있다면 안정성은 어떻게 보장받을 수 있을까?
호출하고자하는 함수의 명세를 살펴보지않으면 파악하기 어려울 수 있다. 그냥 IntelliJ 쓰면 된다
이를 해결하기 위해 호출 예제에서 아래와 같은 주석을 추가하면 어떨까?
1 | /* Java */ |
얼핏 문제는 해결된 것처럼 보일 수 있겠지만, 코틀린은 좀 더 우아하게 처리해줄 수 있다.
아래 코드를 보자.
1 | joinToString(collection, separator = " ", prefix = " ", postfix = ".") |
코틀린으로 작성된 함수를 호출할 때 파라미터에 이름을 아예 지정해서 넘길 수 있다.
참고 코틀린으로 작성되어야만 쓸 수 있다. Java에서 작성된 메소드에는 적용할 수 없다.
다만, 헷갈릴 여지를 남기지 않기 위해서 하나의 파라미터에 이름을 지정하면 모든 파라미터에 이름을 지정해야 한다.
이를 Named arguments
라고 한다.
파라미터 기본값
Java가 안고 있는 문제점 중 하나는 일부 클래스에서 많은 메소드를 오버로딩해서 쓰고 있다는 점이다.
제일 대표적인 예시로 Thread
클래스를 들 수 있다.
이 클래스는 생성자가 무려 8개 존재한다.
이러한 구조는 API를 사용하려는 개발자에게 편의를 제공하기 위한 조치이긴 하지만, 파라미터명과 타입이 반복되며, 동일한 명세 또한 반복해야 한다.
심지어 결과물이 같은데도 말이다. (여기서는 Thread 객체가 될 것이다)
그와 동시에, 일부 파라미터를 생략하는 경우 어떤 결과가 벌어지는 지 명확하지 않다는 문제도 있다.
위와 같은 문제점을 극복할 수 있는 방법으로 코틀린은 함수 선언시 파라미터의 기본값을 지정할 수 있도록 제공한다.
다시 jsonToString
함수를 가져와서 개선해보도록 하자.
여기서 추가할 조건은 대부분의 Collections 요소들이 접두사와 접미사 없이 쉼표로 구분되도록 하는 것이다.
1 | fun <T> joinToString( |
위와 같이 파라미터의 기본값을 지정하고나면 아래와 같이 사용할 수 있다.
1 | >>> joinToString(list, ", ", "", "") |
기존과 같이 모든 파라미터를 넘겨 호출할 수도 있고, 생략을 하더라도 동일한 출력값이 나오는 것을 확인할 수 있다.
이때 파라미터의 순서는 함수의 선언과 동일해야 하며, 뒤에서부터만 생략이 가능하다.
다만 위에서 살펴본 Named arguments
를 활용하는 경우 원하는 파라미터만 원하는 순서대로 지정할 수 있다.
1 | >>> joinToString(list, suffix = ";", prefix = "# ") |
최상위 함수
Java는 객체지향적인 언어로, 모든 코드는 클래스의 메소드 형태로 작성된다.
하지만 그런 지향점과는 다르게, 거의 모든 대규모 프로젝트는 단일 클래스 하나로 동작되지 않는다.
때때로 하나의 작업을 하기 위해 두 개의 클래스가 같이 동작하는 경우도 있고, 기본 객체에 특정 작업이 인스턴스화되어 추가되는 경우도 있다.
코틀린에서는 모든 클래스의 외부에 존재하는 최상위 수준에 직접 함수를 작성할 수 있다.
따라서 아무 클래스에서나 패키지만 추가하면 접근하여 호출할 수 있다.
이번엔 jsonToString
함수를 최상위에 배치해보도록 하자.
package명은 strings
로 문자열 관련 패키지에 직접 산입한 후 join.kt
라는 파일로 저장한다.
1 | package strings |
만약 Java로 짠다면 아래와 같은 코드가 작성될 것이다.
1 | package strings; |
코틀린 컴파일러가 join.kt
를 컴파일하면 위의 클래스명과 동일한 결과물이 나오는 것을 확인할 수 있다.
만약 Java에서 해당 파일을 참조한다면 아래와 같이 사용할 수 있다.
1 | import strings.JoinKt; |
최상위 프로퍼티
함수들처럼 프로퍼티 또한 최상위에 배치할 수 있다. 클래스 외부에 데이터를 개별적으로 젖아하는 것은 가끔씩 유용할 때가 있다.
예를 들어 var
속성에 특정 작업이 몇 번 수행되었는 지를 기록하는 경우 아래와 같이 작성할 수 있다.
1 | var opCount = 0 |
opCount
의 값은 정적 필드에 저장될 것이며, 이 특성을 사용해 공통적으로 사용할 만한 상수를 정의할 수 있다.
1 | val UNIX_LINE_SEPARATOR = "\n" |
기본적으로 최상위에 배치며 프로퍼티들은 다른 일반적인 프로퍼티들과 마찬가지로 Get/Set 등을 통해 Java에서 접근할 수 있다.
1 | const val UNIX_LINE_SEPARATOR = "\n" |
Java 코드에는 아래와 같이 작성한 것과 동일한 값이 제공된다.
1 | public static final String UNIX_LINE_SEPARATOR = "\n"; |