Как намерения обрабатываются в текстовом блоке (Java 13) - PullRequest
2 голосов
/ 20 сентября 2019

Я только что попробовал новую функцию текстового блока в Java 13 и столкнулся с небольшой проблемой.

Я прочитал эту статью из Jaxcenter .

Закрывающая тройная цитатаметки влияют на формат.

String query = """
            select firstName,
            lastName,
            email
            from User
            where id= ?
        """;

System.out.println("SQL or JPL like query string :\n" + query);

Этот формат работает хорошо.Чтобы выровнять с закрывающим разделителем ("" "), многострочная строка оставляет пробелы перед каждой строкой.

Но когда я попытался сравнить следующие две строки текстового блока, они имеют одинаковый формат в консоли вывода,но они не равны даже после stripIntent.

String hello = """
    Hello,
    Java 13
    """;

String hello2 = """
    Hello,
    Java 13
""";

System.out.println("Hello1:\n" + hello);
System.out.println("Hello2:\n" + hello);

System.out.println("hello is equals hello2:" + hello.equals(hello2));

System.out.println("hello is equals hello2 after stripIndent():" + hello.stripIndent().equals(hello2.stripIndent()));

Выходная консоль выглядит так:

hello is equals hello2:false
hello is equals hello2 after stripIndent():false

Я не уверен, где что-то не так, или это текстовый блокцель разработки?

Обновление : просто напечатать hello2 stripIntent,

System.out.println("hello2 after stripIntent():\n" + hello2.stripIndent());

Пробелы перед каждой строкой NOT удалены с помощью stripIntent какожидается.

Обновлено: После прочтения соответствующего документа Java, я думаю, после того, как текстовый блок скомпилирован, он должен был удалить левые намерения строк в блоке. Чтоявляется целью stripIntent для текстового блока? Я знаю, что это легко понять, если использовать его для обычной строки.

Полный код здесь .

Ответы [ 3 ]

5 голосов
/ 20 сентября 2019

Существует понятие случайное пробел .

JEP 355: текстовые блоки (предварительный просмотр)

Обработка во время компиляции

Текстовый блок является константным выражением типа String, как строковый литерал.Однако, в отличие от строкового литерала, содержимое текстового блока обрабатывается компилятором Java в три этапа:

  • Ограничители строки в содержимом переводятся в LF (\ u000A).Цель этого перевода - следовать принципу наименьшего удивления при перемещении исходного кода Java по платформам.

  • Случайное пустое пространство вокруг содержимого, введенное для соответствия отступу Javaисходный код удален.

  • Последовательности Escape в содержимом интерпретируются.Выполнение интерпретации в качестве последнего шага означает, что разработчики могут писать escape-последовательности, такие как \ n, без их изменения или удаления на более ранних этапах.

...

Случайный пробел

Вот пример HTML с использованием точек для визуализации пробелов, добавленных разработчиком для отступа:

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

Поскольку открывающий разделитель обычно расположен для отображения ната же строка, что и оператор или выражение, которое использует текстовый блок, не имеет никакого реального значения для того факта, что 14 визуализированных пробелов начинаются с каждой строки.Включение этих пробелов в содержимое означало бы, что текстовый блок обозначает строку, отличную от строки, обозначенной конкатенированными строковыми литералами.Это повредит миграции и станет источником неожиданности: в подавляющем большинстве случаев разработчик не хочет, чтобы эти пробелы в строке.Кроме того, закрывающий разделитель обычно располагается так, чтобы выровняться с содержимым, что дополнительно предполагает, что 14 визуализируемых пробелов незначительны.
...
Соответственно, подходящей интерпретацией для содержимого текстового блока являетсячтобы отделить случайный пробел в начале и конце каждой строки от необходимого пробела. Компилятор Java обрабатывает содержимое, удаляя случайный пробел, чтобы получить то, что разработчик намеревался.

Вашдопущение, что

    Hello,
    Java 13
<empty line>

равно

....Hello,
....Java 13
<empty line>

является неточным, поскольку это существенно пробелы и они не будут удалены никомпилятор или String#stripIndent.

Чтобы прояснить ситуацию, давайте продолжим представлять случайный пробел в виде точки.

String hello = """
....Hello,
....Java 13
....""";

String hello2 = """
    Hello,
    Java 13
""";

Давайте напечатаем их.

Hello,
Java 13
<empty line>

    Hello,
    Java 13
<empty line>

Давайте назовем String#stripIndent на обоих и распечатаем результаты.

Hello,
Java 13
<empty line>

    Hello,
    Java 13
<empty line>

Чтобы понять, почему ничего не изменилось, нам нужно разобраться в этом.электронная документация.

String#stripIndent

Возвращает строку, значением которой является эта строка, с случайным пробелом , удаленным из начала и концакаждая строка.

Тогда минимальный отступ (мин) определяется следующим образом.Для каждой непустой строки (как определено isBlank()) подсчитываются начальные пробельные символы. Первые пробельные символы в последней строке также учитываются, даже если они пустые. Минимальное значение является наименьшим из этих значений.

Для каждой непустой строки минимальные первые пробельные символыудаляются, а любые завершающие пробелы удаляются.Пустые строки заменяются пустой строкой.

Для обоих String с минимальным отступом является 0.

Hello,          // 0
Java 13         // 0    min(0, 0, 0) = 0 
<empty line>    // 0

    Hello,      // 4
    Java 13     // 4    min(4, 4, 0) = 0
<empty line>    // 0

String#stripIndent предоставляет разработчикам доступ к Java-версии алгоритма повторного отступа, используемого компилятором.

JEP 355

Алгоритм повторного отступа будет нормативнымв спецификации языка Java.Разработчики будут иметь к нему доступ через String::stripIndent, новый метод экземпляра.

Спецификация для JEP 355

Строка, представленная текстовым блоком, не является буквальной последовательностью символов в содержимом.Вместо этого строка, представленная текстовым блоком, является результатом применения следующих преобразований к содержимому в следующем порядке:

  1. Ограничители строки нормализуются к символу ASCII LF (...)

  2. Удален случайный пробел, , как будто при выполнении String::stripIndent для символов в содержимом.

  3. Escape-последовательности интерпретируются, как в строковом литерале.

3 голосов
/ 20 сентября 2019

TLDR.Строки вашего примера не равны, и правильно, что Java сообщает вам, что они не равны.

Подумайте о прочтении описания метода String.stripIndent.Вот парафраз из поста jaxenter.com:

Метод stripIndent удаляет пробелы перед многострочными строками, которые имеют все общие строки, т.е. перемещает весь текст влево, не изменяяформатирование.

Обратите внимание на слова ", которые имеют все общие строки".

Теперь примените ", что все строки имеют общие", к следующей строке:

String hello2 = """
    Hello,
    First, notice that the final line of this example has zero spaces.
    Next, notice that all other lines of this example have non-zero spaces.
"""; // <--- This is a line in the text block.

Ключ отнимается "0! = 3".

2 голосов
/ 20 сентября 2019

Тестирование с jshell:

String hello = """
    Hello,
    Java 13
    """;
hello.replace(" ", ".");

приводит к

"Hello\nJava13\n"

примечание: пробелов вообще нет

String hello2 = """
    Hello,
    Java 13
""";
hello2.replace(" ", ".");

приводит к

"....Hello\n....Java13\n"

Обратите внимание, что оба результата НЕ имеют пробелов в последней строке после последней \n, поэтому stripIndent() не удаляетсяЛюбые пробелы


stripIndent() делает то же самое, что компилятор делает с текстовыми блоками.Пример

String hello3 = ""
    + "    Hello\n"
    + "    Java13\n"
    + "  ";
hello3.stripIndent().replace(" ", ".");

приводит к

"..Hello\n..Java13\n"

, то есть два пробела удалены из всех 3 строк;два пробела, так как в последней строке 2 пробела (в других строках больше, поэтому из всех строк можно удалить не более 2 пробелов)

...