JEP 378: Text Blocks

JEP 378: Text Blocks

Summary

java 언어에 텍스트 블록 을 추가하십시오.
텍스트 블록은 대부분의 이스케이프 시퀀스가 필요하지 않고 예측 가능한 방식으로 문자열의 형식을
자동으로 지정하며 개발자가 원하는 경우 형식을 제어 할 수있는 여러 줄 문자열 리터럴입니다.

History

텍스트 블록은 2019 년 초 에 JEP 355 에서
JEP 326 (Raw String Literals) 에서 시작된 탐색의 후속 작업으로 제안되었습니다.
이는 처음에는 JDK 12를 대상으로했지만 결국 철회되어 해당 릴리스에는 나타나지 않았습니다.
JEP 355는 2019 년 6 월 에 미리보기 기능 으로 JDK 13을 대상 으로했습니다 .
JDK 13에 대한 피드백은 두 개의 새로운 이스케이프 시퀀스를 추가하여 JDK 14에서 텍스트 블록을 다시 미리보기해야한다고 제안했습니다.
결과적으로 JEP 368 은 2019 년 11 월 미리보기 기능으로 JDK 14를 대상 으로했습니다.
JDK 14에 대한 피드백은 텍스트 블록이 추가 변경없이 JDK 15에서 최종적이고 영구적이 될 준비가되었다고 제안했습니다.

Goals

  • 일반적인 경우에는 이스케이프 시퀀스를 피하면서 여러 줄의 소스 코드에 걸쳐있는 문자열을 쉽게 표현할 수 있도록하여 Java 프로그램 작성 작업을 단순화합니다.

  • Java 이외의 언어로 작성된 코드를 나타내는 Java 프로그램에서 문자열의 가독성을 향상시킵니다.

  • 새로운 구문은 문자열 리터럴과 동일한 문자열 집합을 표현하고 동일한 이스케이프 시퀀스를 해석하고 문자열 리터럴처럼 조작 할 수 있도록 규정하여 문자열 리터럴에서 마이그레이션을 지원합니다.

  • 명시 적 공백 및 개행 제어를 관리하기위한 이스케이프 시퀀스를 추가하십시오.

Non-Goals

  • java.lang.String새로운 구문으로 표현 된 문자열에 대해 새로운 참조 유형을 정의하는 것은 목표가 아닙니다 .

  • 별개의 새로운 운영자 정의하는 목표는 아니다 String 에 걸릴 +피연산자.

  • 텍스트 블록은 문자열 보간을 직접 지원하지 않습니다. 향후 JEP에서는 보간이 고려 될 수 있습니다. 그 동안 새로운 인스턴스 메서드 String::formatted는 보간이 필요한 상황에서 도움이됩니다.

  • 텍스트 블록은 원시 문자열, 즉 문자가 어떤 방식으로도 처리되지 않는 문자열을 지원하지 않습니다.

Motivation

Java에서 문자열 리터럴에 HTML, XML, SQL 또는 JSON 스니펫을 임베디드하면
스니펫을 “…”포함하는 코드가 컴파일되기 전에 일반적으로 이스케이프 및 연결로 상당한 편집이 필요합니다.
스니펫은 종종 읽기가 어렵고 유지 관리하기가 어렵습니다.

보다 일반적으로, 텍스트가 다른 프로그래밍 언어의 코드, 골든 파일을 나타내는 구조화 된 텍스트 또는 자연 언어의 메시지인지
여부에 관계없이 Java 프로그램에서 텍스트의 짧은, 중간 및 긴 블록을 표시 할 필요성은 거의 보편적입니다.
한편으로, Java 언어는 무제한 크기와 내용의 문자열을 허용함으로써 이러한 요구를 인식하지만,
반면에 문자열은 소스 파일의 한 줄에 표시 할 수있을 정도로 작아야하는 단순한 설계 기본값을 구현합니다.
쉽게 탈출하기에 충분합니다.
문자열은 소스 파일의 여러 줄에 걸쳐있을만큼 문자열이 충분히 클 수 있다는 점을 수용하고 컨텐츠에서 이스케이프가
개별 문자뿐만 아니라 형식화 및 레이아웃 조작을 나타낼 수 있음을 예상하여 Java 언어를 정규화 할 수 있습니다.

따라서, 광범위한 Java 프로그램의 가독성과 가독성을 향상시켜 문자열 리터럴보다 문자 그대로 여러 줄에 걸쳐서
이스케이프의 시각적 혼란없이 문자열을 나타내는 언어 적 메커니즘을 갖도록합니다.
본질적으로 1 차원 문자 시퀀스가 아닌 2 차원 텍스트 블록입니다.

HTML

1차원

1
2
3
4
5
6
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";

2차원

1
2
3
4
5
6
7
8
9

String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";

SQL

1차원

1
2
3
4
5

String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +
"WHERE `CITY` = 'INDIANAPOLIS'\n" +
"ORDER BY `EMP_ID`, `LAST_NAME`;\n";

2차원

1
2
3
4
5
6

String query = """
SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
WHERE `CITY` = 'INDIANAPOLIS'
ORDER BY `EMP_ID`, `LAST_NAME`;

Polyglot language

1차원

1
2
3
4
5
6
7
8

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval("function hello() {\n" +
" print('\"Hello, world\"');\n" +
"}\n" +
"\n" +
"hello();\n");

2차원

1
2
3
4
5
6
7
8
9
10

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval("""
function hello() {
print('"Hello, world"');
}

hello();
""");

Motivation

Java에서 문자열 리터럴에 HTML, XML, SQL 또는 JSON 스니펫을 임베디드하면
스니펫을 “…”포함하는 코드가 컴파일되기 전에 일반적으로 이스케이프 및 연결로 상당한 편집이 필요합니다.
스니펫은 종종 읽기가 어렵고 유지 관리하기가 어렵습니다.

보다 일반적으로, 텍스트가 다른 프로그래밍 언어의 코드, 골든 파일을 나타내는 구조화 된 텍스트 또는 자연 언어의 메시지인지
여부에 관계없이 Java 프로그램에서 텍스트의 짧은, 중간 및 긴 블록을 표시 할 필요성은 거의 보편적입니다.
한편으로, Java 언어는 무제한 크기와 내용의 문자열을 허용함으로써 이러한 요구를 인식하지만,
반면에 문자열은 소스 파일의 한 줄에 표시 할 수있을 정도로 작아야하는 단순한 설계 기본값을 구현합니다.
쉽게 탈출하기에 충분합니다.
문자열은 소스 파일의 여러 줄에 걸쳐있을만큼 문자열이 충분히 클 수 있다는 점을 수용하고 컨텐츠에서 이스케이프가
개별 문자뿐만 아니라 형식화 및 레이아웃 조작을 나타낼 수 있음을 예상하여 Java 언어를 정규화 할 수 있습니다.

따라서, 광범위한 Java 프로그램의 가독성과 가독성을 향상시켜 문자열 리터럴보다 문자 그대로 여러 줄에 걸쳐서
이스케이프의 시각적 혼란없이 문자열을 나타내는 언어 적 메커니즘을 갖도록합니다.
본질적으로 1 차원 문자 시퀀스가 아닌 2 차원 텍스트 블록입니다.

HTML

1차원

1
2
3
4
5
6
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";

2차원

1
2
3
4
5
6
7
8
9

String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";

SQL

1차원

1
2
3
4
5

String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +
"WHERE `CITY` = 'INDIANAPOLIS'\n" +
"ORDER BY `EMP_ID`, `LAST_NAME`;\n";

2차원

1
2
3
4
5
6

String query = """
SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
WHERE `CITY` = 'INDIANAPOLIS'
ORDER BY `EMP_ID`, `LAST_NAME`;

Polyglot language

1차원

1
2
3
4
5
6
7
8

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval("function hello() {\n" +
" print('\"Hello, world\"');\n" +
"}\n" +
"\n" +
"hello();\n");

2차원

1
2
3
4
5
6
7
8
9
10

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval("""
function hello() {
print('"Hello, world"');
}

hello();
""");

Description

텍스트 블록은 자바 언어에서 문자의 새로운 종류이다. 문자열 리터럴 이 나타날 수 있는 곳 어디에서나 문자열을 나타내는 데 사용될 수 있지만
표현력이 뛰어나고 실수가 덜 복잡합니다.

텍스트 블록은 0 개 이상의 컨텐츠 문자로 구성되며, 구분 기호를 열고 닫는 것으로 묶습니다.

개구 분리는 세 개의 이중 인용 부호 (시퀀스 인 “””줄 종결 뒤에 0 이상의 공백 하였다). 내용은 오프닝 구분 기호의 라인 종결 후 첫 번째 문자에서 시작합니다.

닫기 구분 기호는 세 개의 큰 따옴표 문자의 순서입니다. 내용은 닫는 구분 기호의 첫 번째 큰 따옴표 앞의 마지막 문자에서 끝납니다.

문자열 리터럴의 문자와 달리 내용에는 큰 따옴표 문자가 직접 포함될 수 있습니다.
"텍스트 블록에서 의 사용 은 허용되지만 필요하거나 권장되지는 않습니다. 문자가 이스케이프 처리되지 않고
텍스트 블록을 문자열 리터럴과 시각적으로 구분할 수 있도록 지방 구분 기호 (“”” )를 선택했습니다 “.

문자열 리터럴의 문자와 달리 내용에 줄 종결자가 직접 포함될 수 있습니다.
\n텍스트 블록에서 의 사용 은 허용되지만 필요하거나 권장되지는 않습니다.
예를 들어 텍스트 블록은 다음과 같습니다.

1
2
3
4
5
"""
line 1
line 2
line 3
"""

문자열 리터럴과 같습니다.

1
"line 1\nline 2\nline 3\n"

또는 문자열 리터럴의 연결 :

1
2
3
"line 1\n" +
"line 2\n" +
"line 3\n"

문자열 끝에 줄 종결자가 필요하지 않은 경우 마지막 구분선에 닫는 구분 기호를 배치 할 수 있습니다. 예를 들어 텍스트 블록은 다음과 같습니다.

1
2
3
4
"""
line 1
line 2
line 3"""

문자열 리터럴과 같습니다.

1
2
"line 1\nline 2\nline 3"

텍스트 블록은 빈 문자열을 나타낼 수 있지만 두 줄의 소스 코드가 필요하므로 권장하지 않습니다.

1
2
3
String empty = """
""";

잘못된 형식의 텍스트 블록의 예는 다음과 같습니다.

1
2
3
4
5
6
7
8
9

String a = """"""; // no line terminator after opening delimiter
String b = """ """; // no line terminator after opening delimiter
String c = """
"; // no closing delimiter (text block continues to EOF)
String d = """
abc \ def
"""; // unescaped backslash (see below for escape processing)

컴파일 타임 처리

텍스트 블록은 문자열 리터럴과 같은 유형 의 상수 표현식 입니다 String.
그러나 문자열 리터럴과 달리 텍스트 블록의 내용은 세 가지 단계로 Java 컴파일러에 의해 처리됩니다.

  1. 내용의 줄 종결자는 LF ( \u000A) 로 변환됩니다 . 이 변환의 목적은 플랫폼간에 Java 소스 코드를 이동할 때 가장 놀랄만 한 원칙을 따르는 것입니다.

  2. Java 소스 코드의 들여 쓰기와 일치하도록 도입 된 컨텐츠 주변의 공백이 제거됩니다.

  3. 컨텐츠의 이스케이프 시퀀스가 해석됩니다. 최종 단계로 해석을 수행한다는 것은 개발자 \n가 이전 단계에서 이스케이프 시퀀스 를 수정하거나 삭제하지 않고도 이스케이프 시퀀스를 작성할 수 있음을 의미 합니다.

처리 된 내용은 문자열 리터럴의 문자와 마찬가지로 상수 풀 class의 CONSTANT_String_info항목으로 파일에 기록됩니다 .
class파일이되었는지는 기록하지 않습니다 CONSTANT_String_info항목이 텍스트 블록 또는 문자열 리터럴에서 파생되었다.

런타임에 텍스트 블록은 String문자열 리터럴과 같은 의 인스턴스로 평가됩니다 .
인스턴스 의 String텍스트 블록은 문자열 리터럴에서 파생 된 인스턴스에서 구별에서 그 파생됩니다.
처리 된 내용이 동일한 두 개의 텍스트 블록 은 문자열 리터럴 과 마찬가지로 인턴String 으로 인해 동일한 인스턴스를 참조합니다 .

다음 섹션에서는 컴파일 타임 처리에 대해 자세히 설명합니다.

라인 터미네이터

컨텐츠의 행 종결 자는 Java 컴파일러에 의해 CR ( ) 및 CRLF ( )에서 LF ( )로 정규화 됩니다 .
이렇게하면 소스 코드가 플랫폼 인코딩으로 변환 된 경우에도 컨텐츠에서 파생 된 문자열이 플랫폼에서 동일 합니다.\u000D\u000D\u000A\u000Ajavac -encoding

예를 들어, Unix 플랫폼 (줄 종결자가 LF 인)에서 작성된 Java 소스 코드가 Windows 플랫폼 (줄 종결자가 CRLF 인)에서 편집 된 경우
정규화를 수행하지 않으면 컨텐츠가 각 문자마다 한 문자 씩 길어집니다.
선. 줄 종결자인 LF에 의존하는 알고리즘은 실패 할 수 있으며, 문자열 동등성을 검증하는 데 필요한 테스트 String::equals는 실패합니다.

이스케이프 시퀀스 \n(LF), \f(FF) 및 \r(CR)은 정규화 중에 해석 되지 않습니다 . 이스케이프 처리는 나중에 발생합니다.

우연한 공백

동기 부여의 텍스트 블록은 연결된 문자열 리터럴 문자보다 읽기 쉬웠지만 텍스트 블록의 내용에 대한
명확한 해석에는 포함 된 문자열을 들여 쓰기하기 위해 추가 된 공백이 포함되어있어 시작 구분 기호와 깔끔하게 정렬됩니다.
다음은 점을 사용하여 개발자가 들여 쓰기를 위해 추가 한 공간을 시각화하는 HTML 예제입니다.

1
2
3
4
5
6
7
8
9

String html = """
..............<html>
.............. <body>
.............. <p>Hello, world</p>
.............. </body>
..............</html>
..............""";

여는 분리 문자는 일반적으로 텍스트 블록을 소비하는 명령문 또는 표현식과 동일한 행에 표시되도록 배치되므로 14 개의 시각화 된 공간이
각 행을 시작한다는 사실에는 아무런 의미가 없습니다. 내용에 공백을 포함하면 텍스트 블록이 연결된 문자열 리터럴로 표시된 것과 다른 문자열을 나타냅니다.
이로 인해 마이그레이션이 손상되고 반복되는 놀라움의 원천이 될 것입니다. 개발자가 문자열에서 이러한 공간을 원하지 않을 가능성이 압도적 입니다.
또한, 닫는 구분자는 일반적으로 컨텐츠와 정렬되도록 위치되며, 이는 14 개의 시각화 된 공간이 중요하지 않음을 추가로 제안한다.

또한 각 줄의 끝에 공백이 나타날 수 있습니다.
특히 다른 파일에서 스니펫을 복사하여 붙여 넣기하여 텍스트 블록을 채울 경우
(자세한 내용은 더 많은 파일에서 복사하여 붙여 넣기로 형성되었을 수 있음) 다음은 공백을 시각화하기 위해 점을 사용하여 후행 공백으로
다시 작성된 HTML 예제입니다.

1
2
3
4
5
6
7
8
9

String html = """
..............<html>...
.............. <body>
.............. <p>Hello, world</p>....
.............. </body>.
..............</html>...
..............""";

후행 공백은 대부분 비 의도적이거나 관용적이며 중요하지 않습니다. 개발자가 신경 쓰지 않을 가능성이 압도적입니다.
후행 공백 문자는 소스 코드 편집 환경에서 보이지 않는 아티팩트라는 점에서 줄 종결 자와 비슷합니다.
내용에 포함 된 후행 공백 문자의 존재에 대한 시각적 가이드가 없으면 문자열의 길이,
해시 코드 등에 영향을 줄 수 있기 때문에 반복되는 놀라운 소스가 될 것입니다.

따라서, 텍스트 블록의 내용에 대한 적절한 해석 은 각 줄의 시작과 끝에 우발적 인 공백 을 필수 공백 과 구별하는 것 입니다.
Java 컴파일러는 부수적 인 공백을 제거하여 개발자가 의도 한 내용을 산출함으로써 컨텐츠를 처리합니다.
String::indent원하는 경우 들여 쓰기를 관리하는 데 사용할 수 있습니다. |여백을 시각화하는 데 사용 :

1
2
3
4
5
6
7

|<html>|
| <body>|
| <p>Hello, world</p>|
| </body>|
|</html>|

재 압입 알고리즘 라인 종결 LF로 정규화 된 텍스트 블록의 내용 걸린다.
행 중 하나 이상이 가장 왼쪽 위치에 공백이 아닌 문자를 가질 때까지 각 컨텐츠 행에서 동일한 양의 공백을 제거합니다.
여는 “””문자 의 위치는 알고리즘에 영향을 미치지 않지만 닫는 “””문자 의 위치 는 자체 줄에 배치 된 경우 영향을 미칩니다.
알고리즘은 다음과 같습니다.

  • LF마다 텍스트 블록의 내용을 분할하여 개별 행 목록을 생성하십시오 . 컨텐츠의 LF 인 행은 개별 행 목록에서 빈 행이됩니다.

  • 개별 라인 목록에서 공백이 아닌 라인을 결정 라인 세트에 추가하십시오 .
    (빈 줄-비어 있거나 전체 공백으로 구성된 줄)은 들여 쓰기에 눈에 띄는 영향을 미치지 않습니다.
    결정된 줄 집합에서 빈 줄을 제외하면 알고리즘의 4 단계에서 벗어나지 않습니다.

  • 개별 행 목록의 마지막 행 (즉, 닫는 구분 기호가있는 행)이 공백 인 경우 결정 행 세트에 추가하십시오.
    닫는 구분 기호의 들여 쓰기는 내용 전체의 들여 쓰기에 영향을 주어야합니다 ( “중요 후행”정책).

  • 컴퓨팅 공통의 공백 접두사 각 라인의 선두 공백 문자의 수를 계산하고 최소 수를 고려하여, 선을 결정하는 세트를.

  • 개별 행 목록의 공백이 아닌 각 행에서 공통 공백 접 두부를 제거하십시오 .

  • 5 단계에서 개별 행의 수정 된 목록에있는 모든 행에서 모든 후행 공백을 제거하십시오.
    이 단계는 수정 된 목록에서 전체 공백 행을 축소하여 비어 있지만 삭제하지는 않습니다.

  • LF를 행 사이의 분리 자로 사용하여 6 단계에서 수정 된 개별 행 목록의 모든 행을 결합하여 결과 문자열을 구성하십시오.
    6 단계의 목록에서 마지막 행이 비어 있으면 이전 행의 결합 LF가 결과 문자열의 마지막 문자가됩니다.

이스케이프 시퀀스 \b(백 스페이스) 및 \t(탭)는 알고리즘에 의해 해석 되지 않습니다. 이스케이프 처리는 나중에 발생합니다.

재 들여 쓰기 알고리즘은 Java 언어 사양 에서 규범이됩니다 . 개발자는 String::stripIndent 새로운 인스턴스 메소드 인을 통해 액세스 할 수 있습니다.

중요한 후행 정책

일반적으로 두 가지 방법으로 텍스트 블록의 형식을 지정합니다. 첫째, 첫 번째 “여는 구분 기호 아래에 표시 할 내용의 왼쪽 가장자리를 배치하고
두 번째는 여는 구분 기호 아래에 정확하게 표시되도록 닫는 구분 기호를 자체 줄에 배치합니다.
결과 문자열은 줄의 시작 부분에 공백이 없으며 닫는 구분 기호의 후행 공백 줄을 포함하지 않습니다.

그러나 후행 공백 행은 결정 행으로 간주 되므로이를 왼쪽으로 이동하면 공통 공백 접두어가 줄어들어 모든 행의 시작 부분에서 제거되는 공백의 양이 줄어 듭니다.
닫는 구분 기호가 왼쪽으로 완전히 이동하는 극단적 인 경우에는 일반적인 공백 접두어를 0으로 줄이고 공백 제거를 효과적으로 제거합니다.

예를 들어, 닫는 구분 기호가 왼쪽으로 완전히 이동하면 점으로 시각화 할 공백이 없습니다.

1
2
3
4
5
6
7
8
9

String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";

닫는 구분 기호가있는 후행 공백 행을 포함하여 공통 공백 접두어는 0이므로 각 행의 시작에서 공백이 없습니다.
따라서 알고리즘은 다음을 생성 |합니다 (왼쪽 여백을 시각화하는 데 사용 ).

1
2
3
4
5
|              <html>
| <body>
| <p>Hello, world</p>
| </body>
| </html>

또한, 닫는 구분 기호가 아니라 오히려 아래, 왼쪽으로 끝까지 이동하지 않는 가정 t의 html는 변수 선언보다 더 깊은 팔 개 공간하므로 :

1
2
3
4
5
6
7
8
9

String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";

점으로 표시된 공간은 부수적 인 것으로 간주됩니다.

1
2
3
4
5
6
7
8
9

String html = """
........ <html>
........ <body>
........ <p>Hello, world</p>
........ </body>
........ </html>
........""";

닫는 구분 기호가있는 후행 공백 행을 포함하여 공통 공백 접두어는 8이므로 각 행의 시작에서 8 개의 공백이 제거됩니다.
따라서 알고리즘은 닫는 구분 기호와 관련하여 내용의 필수 들여 쓰기를 유지합니다.

1
2
3
4
5
6
|      <html>
| <body>
| <p>Hello, world</p>
| </body>
| </html>

마지막으로 닫는 구분 기호가 내용 의 오른쪽 으로 약간 이동했다고 가정합니다 .

1
2
3
4
5
6
7
8
9

String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";

점으로 표시된 공간은 부수적 인 것으로 간주됩니다.

1
2
3
4
5
6
7
8
String html = """
..............<html>
.............. <body>
.............. <p>Hello, world</p>
.............. </body>
..............</html>
.............. """;

공통 공백 접두어는 14이므로 각 행의 시작에서 14 개의 공백이 제거됩니다. 마지막 빈 줄은 빈 줄을 남기기 위해 제거되고 마지막 줄은 버려집니다.
즉, 닫는 구분 기호를 내용의 오른쪽으로 이동해도 아무런 영향을 미치지 않으며 알고리즘은 내용의 필수 들여 쓰기를 다시 유지합니다.

1
2
3
4
5
6
|<html>
| <body>
| <p>Hello, world</p>
| </body>
|</html>

탈출 순서

내용을 다시 들여 쓰기 한 후 내용의 이스케이프 시퀀스 가 해석됩니다.
텍스트 블록은 문자열 리터럴 같은 동일한 이스케이프 시퀀스 지원 \n, \t, ', ", 및 \.
전체 목록 은 The Java Language Specification의 3.10.6 단원을 참조하십시오 .
개발자는 새로운 인스턴스 메소드 인을 통해 이스케이프 처리에 액세스 할 수 있습니다 .String::translateEscapes

마지막 단계로 탈출을 해석하는 것은 사용에 개발자 수 \n, \f그리고 \r는
1 단계에서 줄 끝의 번역에 영향을주지 않고 문자열의 수직 형식에 대한,
그리고 사용에 \b그리고 \t그것은 부수적 인 공백의 제거에 영향을주지 않고 문자열의 수평 형식에 대한을
예를 들어, \r이스케이프 시퀀스 (CR) 를 포함하는이 텍스트 블록을 고려하십시오 .

1
2
3
4
5
6
7
8
9

String html = """
<html>\r
<body>\r
<p>Hello, world</p>\r
</body>\r
</html>\r
""";

CR 이스케이프는 라인 터미네이터가 LF로 정규화 될 때까지 처리되지 않습니다.
유니 코드 이스케이프를 사용하여 LF ( \u000A) 및 CR ( \u000D) 을 시각화 하면 결과는 다음과 같습니다.

1
2
3
4
5
6
7

|<html>\u000D\u000A
| <body>\u000D\u000A
| <p>Hello, world</p>\u000D\u000A
| </body>\u000D\u000A
|</html>\u000D\u000A

“텍스트 블록 안에서, 여는 또는 닫는 구분 기호 옆에서도 자유롭게 사용하는 것이 합법적 입니다. 예를 들면 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11

String story = """
"When I use a word," Humpty Dumpty said,
in rather a scornful tone, "it means just what I
choose it to mean - neither more nor less."
"The question is," said Alice, "whether you
can make words mean so many different things."
"The question is," said Humpty Dumpty,
"which is to be master - that's all."
""";

그러나 세 “문자의 시퀀스 “는 닫는 구분 기호를 모방하지 않기 위해 하나 이상의 이스케이프가 필요합니다 .

1
2
3
4
5
6
String code = 
"""
String text = \"""
A text block inside a text block
\""";
""";
1
2
3
4
5
6
7

String tutorial1 =
"""
A common character
in Java programs
is \"""";

1
2
3
4
5
6
7

String tutorial2 =
"""
The empty string literal
is formed from " characters
as follows: \"\"""";

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

System.out.println("""
1 "
2 ""
3 ""\"
4 ""\""
5 ""\"""
6 ""\"""\"
7 ""\"""\""
8 ""\"""\"""
9 ""\"""\"""\"
10 ""\"""\"""\""
11 ""\"""\"""\"""
12 ""\"""\"""\"""\"
""");

새로운 이스케이프 시퀀스

줄 바꿈 및 공백 처리를보다 세밀하게 제어 할 수 있도록 두 개의 새로운 이스케이프 시퀀스를 도입했습니다.

첫째, <line-terminator>이스케이프 시퀀스는 개행 문자의 삽입을 명시 적으로 억제합니다.

예를 들어, 매우 긴 문자열 리터럴을 더 작은 하위 문자열의 연결로 분할 한 다음 결과 문자열 표현식을 여러 줄로 하드 래핑하는 것이 일반적입니다.

1
2
3
4
String literal = "Lorem ipsum dolor sit amet, consectetur adipiscing " +
"elit, sed do eiusmod tempor incididunt ut labore " +
"et dolore magna aliqua.";

<line-terminator>이스케이프 시퀀스를 사용하면 다음과 같이 표현할 수 있습니다.

1
2
3
4
5
String text = """
Lorem ipsum dolor sit amet, consectetur adipiscing \
elit, sed do eiusmod tempor incididunt ut labore \
et dolore magna aliqua.\
""";

문자 리터럴과 기존 문자열 리터럴에서 개행 문자가 포함되지 않는 간단한 이유 때문에
<line-terminator> 이스케이프 시퀀스는 텍스트 블록에만 적용 할 수 있습니다.

둘째, 새 \s이스케이프 시퀀스는 단순히 단일 공백 (\u0020)으로 변환됩니다 .

이스케이프 시퀀스는 우발적 인 공백 제거 이후까지 번역되지 않으므로 \s후행 공백 제거를 방지하는 울타리 역할을 할 수 있습니다.
\s이 예에서 각 행의 끝에 사용하면 각 행의 길이가 정확히 6 자임을 보장합니다.

1
2
3
4
5
6
7

String colors = """
red \s
green\s
blue \s
""";

\s 이스케이프 시퀀스는 텍스트 블록, 기존의 string 리터럴 및 character 리터럴에서 사용할 수 있습니다.

텍스트 블록의 연결

문자열 리터럴을 사용할 수있는 곳이면 어디든지 텍스트 블록을 사용할 수 있습니다. 예를 들어, 텍스트 블록과 문자열 리터럴은 서로 바꿔서 연결할 수 있습니다.

1
2
3
4
5
String code = "public void print(Object o) {" +
"""
System.out.println(Objects.toString(o));
}
""";

그러나 텍스트 블록과 관련된 연결은 다소 번거로울 수 있습니다. 이 텍스트 블록을 시작점으로 사용하십시오.

1
2
3
4
5
String code = """
public void print(Object o) {
System.out.println(Objects.toString(o));
}
""";

유형이 o변수에서 나오도록 변경해야한다고 가정하십시오 . 연결을 사용하면 후행 코드가 포함 된 텍스트 블록을 새 줄에서 시작해야합니다.
불행히도, 아래와 같이 프로그램에 개행을 직접 삽입하면 유형과 텍스트 시작 사이에 공백이 길어집니다 o.

1
2
3
4
5
6
String code = """
public void print(""" + type + """
o) {
System.out.println(Objects.toString(o));
}
""";

공백은 수동으로 제거 할 수 있지만 인용 된 코드의 가독성이 떨어집니다.

1
2
3
4
5
6
7
8

String code = """
public void print(""" + type + """
o) {
System.out.println(Objects.toString(o));
}
""";

클리너 대안은 사용하는 것 String::replace또는 String::format다음과 같이 :

1
2
3
4
5
6
7
8
9
10
11
12

String code = """
public void print($type o) {
System.out.println(Objects.toString(o));
}
""".replace("$type", type);
String code = String.format("""
public void print(%s o) {
System.out.println(Objects.toString(o));
}
""", type);

또 다른 대안은 String::formatted다음과 같이 사용할 수 있는 새로운 인스턴스 메소드를 도입하는 것입니다 .

1
2
3
4
5
6
7

String source = """
public void print(%s object) {
System.out.println(Objects.toString(object));
}
""".formatted(type);

추가 방법

텍스트 블록을 지원하기 위해 다음 방법이 추가됩니다.

  • String::stripIndent(): 텍스트 블록 내용에서 우발적 인 공백을 제거하는 데 사용됩니다.
  • String::translateEscapes(): 이스케이프 시퀀스를 번역하는 데 사용
  • String::formatted(Object… args): 텍스트 블록에서 값 대체를 단순화

Alternatives

아무것도하지 마세요

Java는 줄 바꿈을 피해야하는 문자열 리터럴로 20 년 이상 번성했습니다.
IDE는 여러 줄의 소스 코드에 걸쳐있는 문자열의 자동 서식 및 연결을 지원하여 유지 관리 부담을 덜어줍니다.
이 String클래스는 또한 문자열 을 행 스트림 으로 표시하는 방법과 같이 긴 문자열의 처리 및 서식을 단순화하는 메서드를 포함하도록 발전했습니다 .
그러나 문자열은 Java 언어의 근본적인 부분으로, 문자열 리터럴의 단점은 수많은 개발자 에게 명백합니다..
다른 JVM 언어도 길고 복잡한 문자열이 표시되는 방식을 개선했습니다.
놀랍게도, 다중 라인 문자열 리터럴은 지속적으로 가장 요구되는 Java 기능 중 하나였습니다.
복잡성이 낮거나 중간 정도 인 여러 줄로 구성된 구문을 도입하면 비용이 많이 듭니다.

문자열 리터럴이 여러 줄로 확장되도록 허용

기존의 문자열 리터럴에 줄 종결자를 허용함으로써 Java에서 여러 줄 문자열 리터럴을 도입 할 수 있습니다.
그러나 이것은 “등장 인물 의 고통에 대해서는 아무 것도하지 않습니다 .
코드 스 니펫 빈도로 인해 "이후에 가장 자주 발생하는 이스케이프 시퀀스 \n입니다.
“문자열 리터럴에서 이스케이프를 피하는 유일한 방법 은 문자열 리터럴에 대한 대체 구분 기호 체계를 제공하는 것입니다.
구분자는 JEP 326 (Raw String Literals)에 대해 많이 논의 되었으며, 학습 한 내용은 텍스트 블록의 디자인을 알리는 데 사용되었으므로
문자열 리터럴의 안정성을 뒤집는 데 잘못 안내됩니다.

다른 언어의 다중 문자열 리터럴 채택

여러 줄 문자열 리터럴은 기존 문자열 리터럴에서 줄 종결자를 허용하여 Java에서 간단히 도입 할 수 있습니다.
그러나 이것은 “캐릭터 이스케이프의 고통에 대해서는 아무 효과가 없습니다 . 코드 스 니펫의 빈도로 인해 "
이후 가장 자주 발생하는 이스케이프 시퀀스 \n입니다.
“문자열 리터럴에서 이스케이프를 방지하는 유일한 방법 은 문자열 리터럴에 대한 대체 구분 기호 체계를 제공하는 것입니다.
구분 기호는 JEP 326 (Raw String Literals)에 대해 많이 논의되었으며
학습 한 교훈은 텍스트 블록의 디자인에 정보를 제공하는 데 사용되었으므로 문자열 리터럴의 안정성을 혼란스럽게 만들었습니다.

많은 사람들이 Java가 Swift 또는 Rust의 여러 줄 문자열 리터럴을 채택해야한다고 제안했습니다.
그러나 “언어 X가하는 일을하는 것”의 접근 방식은 본질적으로 무책임합니다.
모든 언어의 거의 모든 기능은 해당 언어의 다른 기능으로 구성됩니다.
대신, 게임은 다른 언어가하는 일을 통해 배우고, 그들이 선택한 트레이드 오프 (명시 적 및 암시 적)를 평가하고,
우리가 가진 언어의 제약 조건과 커뮤니티 내 사용자의 기대에 적용 할 수있는 질문을하는 것입니다.

들어 JEP 326 (원시 문자열 리터럴), 우리는 많은 현대적인 프로그래밍 언어와 여러 줄 문자열 리터럴에 대한 지원을 대상으로 설문 조사를 실시했습니다.
이러한 조사의 결과는 “분리 문자에 대한 세 문자 선택 (이 선택에 대한 다른 이유도 있었음) 및
자동 들여 쓰기 관리의 필요성 인식과 같은 현재 제안에 영향을 미쳤습니다.

우발적 인 공백 제거

Java가 부수적 인 공백을 자동으로 제거하는 기능을 지원하지 않고 여러 줄 문자열 리터럴을 도입 한 경우
많은 개발자가 자체 공백을 제거하는 메소드를 작성하거나 String클래스가 제거 메소드를 포함하도록 로비합니다 .
그러나 이는 문자열이 런타임에 인스턴스화 될 때마다 잠재적으로 값 비싼 계산을 의미하므로 문자열 인터 닝의 이점이 줄어 듭니다.
Java 언어를 사용하면 선행 및 후행 위치에서 우발적 인 공백 제거가 가장 적합한 솔루션 인 것 같습니다.
개발자는 닫는 구분 기호를주의해서 배치하여 공백을 제거 할 수 있습니다.

후행 공백 제거를 거부 할 수 없으므로 후행 공백이 중요한 드문 경우 (예 : Markdown 의
태그에 공백 두 개 )
개발자는 8 진과 같이 포함을 강제로 포함시키기 위해 특별한 조치를 취해야합니다.
이스케이프 시퀀스\040 (ASCII 문자 32, 공백) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14

"""
The quick brown fox\040\040
jumps over the lazy dog
"""
"""
The quick brown fox""" + " \n" + """
jumps over the lazy dog
"""
"""
The quick brown fox |
jumps over the lazy dog
""".replace("|", "");

원시 문자열 리터럴

들어 JEP 326 (원시 문자열 리터럴), 우리는 문자열의 원시 다움에 초점을 탈출 줄 바꿈 및 따옴표없이 문자열을 나타내는 문제에 대한 다른 접근을했다.
우리는 이제 원시 문자열 리터럴이 여러 줄의 소스 코드에 쉽게 확장 될 수 있지만 내용에서
이스케이프되지 않은 구분 기호를 지원하는 비용이 극도로 높았 기 때문에 이러한 초점이 잘못되었다고 생각합니다.
이는 다중 라인 사용 사례에서 기능의 효율성을 제한했습니다. 이는 Java 프로그램에 다중 라인 (그러나 실제 코드는 아님)
코드 스 니펫을 임베드하는 빈도로 인해 중요합니다. 원시 (raw-ness)에서 여러 줄 (multi-line-ness)로 피봇 한 결과는 문자열 리터럴,
텍스트 블록 및 향후 추가 될 관련 기능간에 일관된 이스케이프 언어를 사용하는 데 중점을 두었습니다.

테스팅

String텍스트 블록을 사용하려면 인스턴스 작성, 인터 닝 및 조작에 문자열 리터럴을 사용하는 테스트를 복제해야합니다.
라인 터미네이터 및 EOF와 관련된 코너 케이스에 대해서는 네거티브 테스트를 추가해야합니다.

텍스트 블록에 Java-in-Java, Markdown-in-Java, SQL-in-Java 및 하나 이상의 JVM-language-in-Java가 포함되도록 테스트를 추가해야합니다.

참조