KB 07 문자열 — 템플릿과 원시 문자열
고친 사람 github-actions[bot]
0. 자바의 + 연결이 코틀린에서는 문자열 안의 식이 된다
자바에서 문자열 사이에 값을 끼우려면 + 로 잇거나 String.format 을 씁니다. 끼울 값이 늘수록 따옴표와 + 가 번갈아 나와 원래 문장이 잘 안 보입니다.
System.out.println("" + (a + b));
System.out.println("a + b = " + (a + b));
코틀린은 같은 일을 문자열 안에서 합니다. 따옴표를 닫았다 다시 여는 대신 식을 그 대목에 바로 적습니다.
fun show(a: Int, b: Int) {
println("${a + b}") // 7
println("a + b = ${a + b}") // a + b = 7
}
fun main() {
show(3, 4)
}
fun show(a: Int, b: Int)— 함수 선언입니다. 인자는 이름 뒤에 콜론, 그 뒤에 타입을 씁니다.Int는 자바의int에 해당합니다"${a + b}"— 문자열 템플릿(string template)입니다. 문자열 안의${ }에 식을 쓰면, 그 값이 문자열로 바뀌어 끼워집니다println— 한 줄 출력입니다. 앞에System.out.이 없습니다. 줄 끝에 세미콜론도 없습니다fun main()— 프로그램 시작점입니다.String[] args를 받지 않아도 됩니다
Sum.kt 로 저장해 코틀린 컴파일러 kotlinc 로 컴파일했습니다. -d out 은 결과물을 out 폴더에 쓰라는 옵션입니다.
컴파일한 결과는 kotlin 명령으로 돌렸습니다. classpath 는 실행할 때 클래스를 찾을 경로 목록입니다. kotlin 은 java 와 같은 일을 하되, 코틀린 표준 라이브러리를 이 목록에 스스로 넣어 줍니다.
출력은 전부 Kotlin 2.4.10, JDK 21 에서 직접 돌린 결과입니다.
$ kotlinc Sum.kt -d out
$ kotlin -cp out SumKt
SumKt 는 쓴 적 없는 이름입니다. 클래스 밖, 파일 바로 아래에 쓴 함수를 톱레벨(top-level) 함수라고 부릅니다.
JVM(java 명령이 띄우는 자바 가상 머신)에서는 모든 메서드가 어느 클래스엔가 속해야 합니다. 그래서 컴파일러가 파일 이름 + Kt 클래스를 짓고 톱레벨 함수를 그 안의 static 메서드로 넣습니다.
확인 — 템플릿은 컴파일할 때 연결 코드가 된다
여기까지는 두 언어의 소스를 나란히 놓고 한 대조입니다. 코틀린 템플릿이 정말 자바의 연결과 같은 것을 만드는지는 컴파일 결과를 열어 보면 끝납니다.
디컴파일러는 컴파일 결과인 .class 파일을 거꾸로 읽어 자바 코드로 다시 적는 도구입니다. 이 편에서는 CFR 0.152 를 쓰는데, jar 하나로 된 도구라 java -jar cfr.jar 클래스파일 로 돌립니다.
$ java -jar cfr.jar out/SumKt.class
...
public final class SumKt {
public static final void show(int a, int b) {
System.out.println((Object)String.valueOf(a + b));
System.out.println((Object)("a + b = " + (a + b)));
}
...
}
볼 것은 두 줄입니다.
- 템플릿만 있는
"${a + b}"는String.valueOf(a + b)가 됐습니다 - 앞에 글자가 붙은
"a + b = ${a + b}"는 자바의+연결이 됐습니다
앞에서 본 자바 두 줄을 담은 Sum.java 를 JDK 21 의 javac 로 컴파일해 CFR 에 넣으면 "" + (n + n2) 와 "a + b = " + (n + n2) 가 나옵니다. 코틀린 템플릿이 자바의 문자열 연결과 같은 모양으로 돌아왔습니다.
맨 위의 머리 주석과 import, 한 줄이 아주 긴 @Metadata(...) 는 ... 로 줄였습니다. @Metadata 는 코틀린 컴파일러가 클래스에 남기는 메타데이터입니다. main 두 개도 이 편의 관심사가 아니라 줄였습니다.
디컴파일 결과는 원래 소스를 되살린 것이 아니라, 클래스 파일에 적힌 내용을 자바 문법으로 다시 적은 것입니다. (Object) 캐스트가 그 흔적입니다. 클래스 파일이 println(Object) 를 부르고 있어서 CFR 이 그 사실을 캐스트로 드러냈습니다.
템플릿은 실행할 때 해석되는 형식 문자열이 아닙니다. 컴파일할 때 문자열 연결 코드로 바뀝니다.
1. 템플릿 — $name, ${식}, 그리고 $ 자체
템플릿은 $ 한 글자로 시작합니다. 그래서 컴파일러가 $ 뒤를 어디까지 이름으로 읽는지 알아야 엉뚱한 출력을 피합니다.
Tmpl.kt 에 일곱 줄을 찍었습니다.
fun main() {
val name = "kotlin"
println("hi $name") // hi kotlin
println("len ${name.length}") // len 6
println("$name.length") // kotlin.length
println("${name}s") // kotlins
println("cost \$name") // cost $name
println("cost ${'$'}name") // cost $name
println("price $9") // price $9
}
val name = "kotlin"— 다시 대입할 수 없는 변수입니다. 타입을 안 적으면 값에서String을 알아냅니다$name— 이름 하나만 끼울 때는 중괄호를 뺍니다. 식이 필요하면${ }로 감쌉니다name.length— 자바의name.length()에 해당합니다. 코틀린은 괄호 없이 읽습니다\$와${'$'}—$를 글자로 쓰는 두 방법입니다.'$'는 작은따옴표로 쓰는 문자(Char) 하나입니다. 뒤쪽은 그 문자를 템플릿으로 끼운 것입니다
$ kotlinc Tmpl.kt -d out
$ kotlin -cp out TmplKt
셋째 줄이 함정입니다. "$name.length" 는 6 이 아니라 kotlin.length 를 찍었습니다.
중괄호 없는 $ 는 이름 하나만 읽습니다. 점을 만나면 거기서 이름이 끝납니다. 그래서 .length 는 뒤에 붙은 글자가 됩니다. 이름 바로 뒤에 글자를 붙이고 싶을 때도 "${name}s" 처럼 중괄호로 끊습니다.
마지막 줄의 "price $9" 는 에러 없이 price $9 를 찍었습니다. $ 뒤가 이름으로 시작할 수 없는 글자라서 템플릿으로 읽히지 않았습니다.
CFR 로 되돌리면 셋째 줄은 name + ".length" 로 컴파일돼 있습니다. 컴파일러가 처음부터 그렇게 읽었다는 증거입니다. \$·${'$'}·$9 가 든 아래 세 줄은 연결 없이 상수 문자열 "cost $name"·"price $9" 가 됐습니다. 컴파일할 때 $ 글자로 확정된 것입니다.
$ 가 많이 드는 문자열이라면 따옴표 앞에 $$ 를 붙이는 방법도 있습니다. 그러면 $$name 만 템플릿이 되고 $name 은 글자로 남습니다. println($$"cost $name, $$name") 은 2.4.10 에서 경고 없이 컴파일돼 cost $name, kotlin 을 찍었습니다.
$이름 은 이름 하나까지만 읽고 그 밖의 식은 ${ } 로 감쌉니다. $ 를 글자로 쓰려면 \$ 나 ${'$'} 를 씁니다.
자바에는 템플릿이 없다
자바 쪽을 봅니다. JDK 21 에는 String Templates 라는 기능이 미리보기(preview) 기능으로 들어 있었습니다. 미리보기 기능은 정식으로 확정되기 전이라 --enable-preview 를 켜야 쓸 수 있습니다. 이 기능은 JDK 22 에서 한 번 더 미리보기를 거친 뒤, 세 번째 미리보기안(JEP 465)이 철회되면서 JDK 23 에서 빠졌습니다.
public class Str {
public static void main(String[] args) {
String name = "java";
System.out.println("hi " + name);
System.out.println("hi %s".formatted(name));
System.out.println(STR."hi \{name}");
}
}
앞의 두 줄이 자바에서 평소에 쓰는 방법입니다. 마지막 줄의 STR."...\{name}" 이 String Templates 문법입니다. 자바 결과물은 jout 폴더에 따로 둡니다.
$ javac -d jout Str.java
Str.java:6: error: string templates are a preview feature and are disabled by default.
System.out.println(STR."hi \{name}");
^
(use --enable-preview to enable string templates)
1 error
옵션 없이는 컴파일되지 않습니다. 그래서 JDK 21 에서도, 이 기능이 빠진 JDK 23 에서도 자바에서 문자열에 값을 끼우는 기본 수단은 + 와 formatted(또는 String.format)입니다. formatted 는 실행할 때 %s 를 찾아 바꾸는 반면, 코틀린 템플릿은 0절에서 봤듯이 컴파일할 때 연결 코드가 됩니다.
2. """ 원시 문자열과 trimIndent, trimMargin
여러 줄짜리 SQL 이나 JSON 을 코드에 넣을 때 쓰는 문법입니다. 자바의 텍스트 블록과 겉모양이 같은데, 들여쓰기를 걷는 주체가 다릅니다.
fun main() {
val name = "kotlin"
val path = """C:\new\table"""
val a = """
hello
$name
""".trimIndent()
val b = """
|hello
| world
""".trimMargin()
println(path)
println(a)
println(b)
println("""\$name ${'$'}name""")
}
네 번의 println 이 여섯 줄을 찍었습니다. a 와 b 가 두 줄씩이라 옆에 붙이지 못하고 그대로 싣습니다. 첫 줄이 path, 다음 두 줄이 a, 그다음 두 줄이 b, 마지막 줄이 템플릿을 넣은 원시 문자열입니다.
$ kotlinc Raw.kt -d out
$ kotlin -cp out RawKt
C:\new\table
hello
kotlin
hello
world
\kotlin $name
"""..."""— 원시 문자열(raw string)입니다. 역슬래시를 이스케이프로 처리하지 않고, 줄바꿈도 담습니다. 한 줄에도 씁니다.trimIndent()— 모든 줄에 공통으로 붙은 가장 짧은 들여쓰기를 걷습니다. 첫 줄과 마지막 줄이 공백뿐이면 그 줄도 지웁니다.trimMargin()— 줄마다 앞 공백과|까지를 걷습니다. 첫 줄과 마지막 줄이 공백뿐이면 지우는 것은trimIndent()와 같습니다.|대신 다른 문자열을 인자로 줄 수 있습니다- 템플릿은 원시 문자열 안에서도 됩니다. 마지막 줄은
\와$가 원시 문자열에서 어떻게 읽히는지 보려고 넣었습니다
C:\new\table 의 \n 과 \t 가 줄바꿈이나 탭이 되지 않고 글자로 남았습니다. a 는 모든 줄에 공통으로 붙은 들여쓰기가 걷혀 둘째 줄의 두 칸만 남았습니다. b 는 줄 앞의 공백과 | 까지가 걷혀 역시 둘째 줄의 두 칸만 남았습니다.
마지막 줄이 주의할 대목입니다. 원시 문자열에서 \ 는 이스케이프가 아니라 글자라서, \$name 은 역슬래시 뒤에 템플릿 $name 이 붙은 것으로 읽혔습니다. 원시 문자열에서 $ 글자가 필요하면 ${'$'} 를 씁니다.
눈여겨볼 것은 .trimIndent() 와 .trimMargin() 이 언어 문법이 아니라 코틀린 표준 라이브러리 함수라는 점입니다. 들여쓰기를 걷는 일을 코틀린은 함수에 맡겼습니다. 자바는 어떻게 하는지 봅니다.
자바 텍스트 블록과 대조
같은 일을 JDK 15 부터 정식 기능인 텍스트 블록으로 썼습니다(JEP 378). 템플릿이 없으니 %s 를 두고 formatted 로 끼웁니다.
public class Text {
public static void main(String[] args) {
String name = "java";
String a = """
hello
%s
""".formatted(name);
System.out.print(a);
}
}
들여쓰기를 걷는 호출이 소스 어디에도 없습니다. 그럼 소스에 적힌 들여쓰기는 언제 없어지는지, 이 자바 클래스 파일만 CFR 로 열어 봅니다.
$ javac -d jout Text.java
$ java -jar cfr.jar jout/Text.class
...
String string2 = "hello\n %s\n".formatted(string);
...
string·string2 는 CFR 이 지어 붙인 변수 이름입니다. 이 클래스 파일에는 지역 변수 이름이 남지 않았습니다.
자바는 들여쓰기 걷기가 언어 규칙이라 javac 가 컴파일할 때 끝냅니다. 클래스 파일에는 이미 걷힌 문자열이 들어 있고, 부르는 함수가 따로 없습니다. 닫는 """ 를 따로 한 줄에 둬서 끝에 \n 이 남은 것도 코틀린 trimIndent() 와 다릅니다.
| 코틀린 원시 문자열 | 자바 텍스트 블록 | |
|---|---|---|
| 한 줄로 쓰기 | 된다 | 안 된다. 여는 """ 뒤에 줄바꿈이 없으면 missing line terminator 에러 |
\n 같은 이스케이프 |
처리 안 함. 글자로 남음 | 처리함 |
| 들여쓰기 걷기 | trimIndent() · trimMargin() 함수를 부른다 |
언어 규칙. 컴파일 때 끝남 |
| 값 끼우기 | 템플릿 $name |
formatted 로 실행 때 |
코틀린 원시 문자열은 이스케이프를 안 하는 문자열입니다. 들여쓰기는 언어가 아니라 함수가 걷습니다. 그래서 원시 문자열을 어디에 넘기든 걷는 규칙을 trimIndent 와 trimMargin 중에서 고를 수 있습니다.
3. 비교 — == 가 곧 equals
자바에서 문자열을 == 로 비교하는 것은 흔한 버그입니다. 코틀린에서는 거꾸로 == 가 내용을 비교하는 기본 방법입니다.
fun main() {
val a = "kotlin"
val b = "KOTLIN".lowercase()
val n: String? = null
println(a == b) // true
println(a === b) // false
println(
a.equals("KOTLIN", ignoreCase = true)
) // true
println(n == a) // false
}
"KOTLIN".lowercase()— 소문자로 바꾼 새 문자열을 실행할 때 만듭니다.a와 내용은 같지만 다른 객체를 만들려고 썼습니다. 언어별 대소문자 규칙(로케일)을 타지 않는 자바의toLowerCase(Locale.ROOT)에 해당합니다===— 같은 객체인지 보는 참조 비교입니다. 자바의==에 해당합니다String?— 타입 뒤의?는null을 담을 수 있다는 표시입니다.?없는String에는null을 넣을 수 없습니다ignoreCase = true— 이름 붙인 인자(named argument)입니다. 인자 이름을 적고 값을 넘깁니다
$ kotlinc Cmp.kt -d out
$ kotlin -cp out CmpKt
내용이 같은 a == b 는 true, 객체가 다른 a === b 는 false 입니다. null 인 n 을 왼쪽에 둔 n == a 도 예외 없이 false 를 냈습니다.
같은 네 줄을 자바로 썼습니다. 코틀린 == 에 해당하는 것은 equals 이고, 왼쪽이 null 일 수 있으면 Objects.equals 입니다.
import java.util.Locale;
import java.util.Objects;
public class Cmp {
public static void main(String[] args) {
String a = "kotlin";
String b = "KOTLIN".toLowerCase(Locale.ROOT);
String n = null;
System.out.println(a == b);
System.out.println(a.equals(b));
System.out.println(a.equalsIgnoreCase("KOTLIN"));
System.out.println(Objects.equals(n, a));
}
}
$ javac -d jout Cmp.java
$ java -cp jout Cmp
false
true
true
false
두 출력의 첫 줄이 갈렸습니다. 같은 a == b 인데 코틀린은 true, 자바는 false 입니다. 자바에서 false 를 낸 것과 같은 비교를 코틀린에서 하려면 등호를 하나 더 붙여 a === b 를 써야 하고, 그 줄이 코틀린 출력의 둘째 줄 false 입니다.
나머지 두 줄은 이름만 다르고 결과가 같습니다. 네 줄을 짝지어 놓으면 이렇습니다.
| 하려는 비교 | 자바 | 코틀린 | 두 출력 |
|---|---|---|---|
| 내용이 같은가 | a.equals(b) |
a == b |
둘 다 true |
| 같은 객체인가 | a == b |
a === b |
둘 다 false |
| 대소문자 무시 | a.equalsIgnoreCase("KOTLIN") |
a.equals("KOTLIN", ignoreCase = true) |
둘 다 true |
왼쪽이 null |
Objects.equals(n, a) |
n == a |
둘 다 false |
마지막 줄이 코틀린 == 의 성격을 드러냅니다. 자바에서 n.equals(a) 라고 썼다면 NullPointerException 이 났을 텐데, 코틀린 n == a 는 아무 준비 없이도 false 를 냈습니다. 자바에서 그 안전을 얻으려면 Objects.equals(n, a) 로 감싸야 합니다.
정리하면, 코틀린의 == 는 null 을 가려 주는 equals 이고 === 가 자바의 == 입니다. 대소문자를 무시할 때는 equals(other, ignoreCase = true) 를 씁니다. 직접 만든 클래스까지 포함한 비교 전반은 뒤에 나올 비교 편에서 다룹니다.
4. 자주 쓰는 것의 자바 대응표
charAt·split·parseInt 는 코틀린에서 뭐라고 부를까요. 대부분 이름만 바뀌었는데, split 하나는 결과가 달라집니다.
"a.b.c".split(".") 을 자바는 빈 배열로, 코틀린은 [a, b, c] 로 돌려줍니다. 코틀린 쪽부터 봅니다.
fun main() {
val s = "a.b.c"
val first = s[0]
val tail = s.substring(2)
val parts = s.split(".")
val n = "42".toInt()
val m = "4x".toIntOrNull()
println(first) // a
println(tail) // b.c
println(parts) // [a, b, c]
println(n) // 42
println(m) // null
println("4x".toInt()) // 예외 — 아래
}
s[0]— 인덱스로 글자 하나를 꺼냅니다. 결과 타입은Char, 자바의char입니다s.split(".")—String을 넘기면 정규식이 아니라 문자열 그대로 찾습니다. 정규식으로 자르려면 정규식 객체Regex를 넘깁니다. 결과는List<String>이고,List는 순서가 있는 컬렉션입니다split은ignoreCase·limit처럼 기본값이 정해진 인자(기본 인자)를 더 받습니다. 소스에서 안 적으면 그 기본값이 쓰입니다toInt()— 문자열을 정수로 바꿉니다. 숫자가 아니면 예외가 납니다toIntOrNull()— 숫자가 아니면 예외 대신null을 돌려줍니다. 그래서 반환 타입이Int?입니다
$ kotlinc Ops.kt -d out
$ kotlin -cp out OpsKt
...
Exception in thread "main" java.lang.NumberFormatException: For input string: "4x"
...
예외의 호출 경로는 ... 로 줄였습니다. "4x".toIntOrNull() 은 null 을, "4x".toInt() 는 NumberFormatException 을 냈습니다.
예외 이름이 java.lang.NumberFormatException 인 것을 볼 것입니다. 코틀린이 따로 만든 예외가 아니라 자바가 숫자 변환에 쓰는 바로 그 예외입니다. toInt() 는 이름만 코틀린식일 뿐 하는 일도 실패하는 방식도 자바와 같습니다.
자바로 같은 문자열을 잘랐습니다.
import java.util.Arrays;
public class Ops {
public static void main(String[] args) {
String s = "a.b.c";
String[] dot = s.split(".");
String[] escaped = s.split("\\.");
System.out.println(Arrays.toString(dot));
System.out.println(Arrays.toString(escaped));
}
}
$ javac -d jout Ops.java
$ java -cp jout Ops
[]
[a, b, c]
charAt·substring·parseInt 는 자바 개발자에게 익숙하니 split 만 옮겼습니다. 같은 split(".") 인데 코틀린은 [a, b, c], 자바는 [] 입니다.
자바의 split 은 인자를 정규식으로 읽습니다. 정규식 . 은 아무 글자 하나라서 모든 글자가 구분자가 됐습니다. 사이사이 남은 빈 문자열은 뒤에서부터 버려져 배열이 비었습니다. 점으로 자르려면 "\\." 로 이스케이프해야 합니다.
거꾸로 코틀린에서 자바 버릇대로 쓰면 어떨까요. "a.b.c".split("\\.") 은 역슬래시와 점 두 글자를 찾다가 못 찾아 [a.b.c] 를 돌려줬습니다. 옮겨 올 때 가장 조심할 줄입니다.
정규식이 필요하면 split(Regex("\\.")) 처럼 Regex 로 넘깁니다. 이렇게 넘기면 [a, b, c] 가 나왔습니다. 빈 문자열도 다르게 다룹니다. 코틀린 split 은 끝의 빈 문자열을 버리지 않아서, "a,b,,".split(",") 이 코틀린에서는 [a, b, , ], 자바에서는 [a, b] 였습니다.
| 하는 일 | 자바 | 코틀린 |
|---|---|---|
| 글자 하나 | s.charAt(0) |
s[0] |
| 뒷부분 자르기 | s.substring(2) |
s.substring(2) |
| 쪼개기 | s.split("\\.") → String[], 정규식, 끝의 빈 문자열 버림 |
s.split(".") → List<String>, 문자열 그대로, 끝의 빈 문자열 유지. 정규식은 Regex 로 |
| 정수로 | Integer.parseInt("42") |
"42".toInt() |
| 실패하면 null | 없음. try-catch 로 감쌈 |
"4x".toIntOrNull() |
대부분은 이름만 바뀐 같은 동작입니다. 예외는 둘입니다. split 에 넘긴 String 은 정규식으로 읽히지 않고, 끝의 빈 문자열도 남습니다. toIntOrNull 은 예외 대신 null 을 돌려주는, 자바에 짝이 없는 코틀린 쪽 함수입니다.
5. StringBuilder 와 buildString
반복문으로 문자열을 쌓을 때 자바는 StringBuilder 를 씁니다. 코틀린에서도 같은 클래스를 StringBuilder() 로 만들어 씁니다(new 는 없습니다). 여기에 만들기부터 toString() 까지 한 번에 하는 buildString 이 더해집니다.
fun main() {
val s = buildString {
append("n=")
for (i in 1..3) {
append(i)
}
}
println(s) // n=123
}
buildString { ... }— 중괄호 블록은 람다입니다. 람다는 이름 없는 함수를 값으로 넘긴 것입니다- 함수 호출의 마지막 인자가 람다면 괄호 밖으로 뺄 수 있습니다. 이렇게 쓴 람다를 후행 람다(trailing lambda)라고 부르고, 남은 괄호가 비면 괄호도 뺍니다
append("n=")— 앞에sb.이 없습니다. 블록 안에서this가 새StringBuilder이기 때문입니다. 이런 람다를 수신 객체 지정 람다(lambda with receiver)라고 부릅니다for (i in 1..3)—1..3은 1 부터 3 까지(3 포함)의 범위입니다. 자바의for (int i = 1; i <= 3; i++)에 해당합니다- 블록이 끝나면
buildString이 쌓인 내용의toString()결과를 돌려줍니다
$ kotlinc Build.kt -d out
$ kotlin -cp out BuildKt
이제 같은 값을 만드는 자바 Build.java 를 옆에 둡니다.
public class Build {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("n=");
for (int i = 1; i <= 3; i++) {
sb.append(i);
}
String s = sb.toString();
System.out.println(s);
}
}
javac 로 컴파일해 돌리면 자바도 n=123 을 찍습니다. 같은 값인데 코틀린 쪽에는 없는 것이 셋입니다. StringBuilder 를 만드는 new StringBuilder(), 그것을 담아 두는 변수 sb, 마지막에 부르는 toString() 입니다.
셋은 전부 buildString 이 대신합니다. 블록에 들어가기 전에 새 StringBuilder 를 만들어 블록 안의 this 로 주고, 블록이 끝나면 그것의 toString() 결과를 돌려줍니다. 그래서 블록 안에는 append 만 남습니다.
정리하면, buildString 은 새 문법이 아니라 StringBuilder 를 만들고 toString() 으로 끝내는 틀을 줄여 주는 함수입니다. 표준 라이브러리에 inline 으로 선언돼 있는데, 인라인 함수는 부르는 곳에 함수 본문과 넘긴 람다를 복사해 넣도록 표시한 함수라 람다 객체가 따로 만들어지지 않습니다.
6. 한 장 요약
코틀린 문자열 템플릿은 실행할 때 해석되는 형식 문자열이 아니라 컴파일할 때 만들어지는 문자열 연결 코드다.
split에 넘긴String은 정규식이 아니다.
| 하는 일 | 자바 (JDK 21) | 코틀린 | 확인한 방법 |
|---|---|---|---|
| 값 끼우기 | "a + b = " + (a + b) · formatted |
"a + b = ${a + b}". $name.length 는 이름까지만 읽음 |
실행 · CFR |
$ 글자 |
해당 없음 | \$ · ${'$'} · $$"..." |
실행 · CFR |
| 여러 줄 문자열 | 텍스트 블록. 한 줄 불가, 이스케이프 처리 | 원시 문자열. 한 줄 가능, 이스케이프 안 함 | javac 에러 · 실행 |
| 들여쓰기 걷기 | 언어 규칙. 컴파일 때 끝남 | trimIndent() · trimMargin() 함수를 부름 |
실행 · CFR |
| 내용 비교 | a.equals(b) · Objects.equals(a, b) |
a == b |
양쪽 실행 |
| 참조 비교 · 대소문자 무시 | a == b · equalsIgnoreCase |
a === b · equals(b, ignoreCase = true) |
양쪽 실행 |
| 글자 하나 · 자르기 · 정수로 | charAt · substring · parseInt |
s[0] · substring · toInt() |
실행 |
| 쪼개기 · 실패하면 null | split("\\.") 정규식 · 없음 |
split(".") 에 넘긴 String 은 정규식 아님 · toIntOrNull() → Int? |
양쪽 실행 |
| 쌓아서 만들기 | StringBuilder + toString() |
buildString { append(...) } |
양쪽 실행 |
관련 항목
문자열에 값을 끼우는 문법과 그 대안
문자열 템플릿 · 문자열 연결 · String.format · String Templates · 다중 달러 문자열 보간 · 이스케이프 시퀀스
여러 줄 문자열을 적는 문법과 들여쓰기를 걷는 함수
원시 문자열 · 텍스트 블록 · trimIndent · trimMargin
문자열 비교에 쓰는 연산자와 메서드
동등성 비교 · 참조 비교 · equals · Objects.equals · equalsIgnoreCase
자바 String 메서드와 짝을 이루는 코틀린 문자열 함수
charAt · substring · split · 정규 표현식 · Integer.parseInt · toIntOrNull · NumberFormatException
문자열을 쌓아 만드는 것
StringBuilder · buildString · 람다 · 수신 객체 지정 람다 · 인라인 함수
이 편의 코드를 이루는 그 밖의 코틀린 문법 요소
val · 널 가능 타입 · 이름 붙인 인자 · 기본 인자 · 범위 연산자
컴파일 결과를 열어 보는 도구와 거기 남는 것
디컴파일 · 디컴파일러 · CFR · @Metadata · 지역 변수 테이블