Статьи

Пять золотых галочек

Пять видов струн

Начиная с Java 1.0 у нас были строковые литералы "like this" . Какие еще строки мы могли бы хотеть? Другие языки программирования дают нам:

  • Интерполяция выражения: s"I am ${age - 10} years old."
  • Интерполяция с форматированием: f"Price: $price%8.2f"
  • Строки с внутренним синтаксисом, который проверяется во время компиляции: r"[0-9]+([.,][0-9]*)? Или xml"<a href='http://java.sun.com'>The Java home page</a>"
  • Необработанные строки, в которых обратные косые черты не экранированы: raw"\.*"
  • Многострочные строки, которые могут содержать переводы строк:
    1
    2
    3
    4
    5
    """
    +-----+
    | Cay |
    +-----+
    """

Здесь я использую синтаксис, который напоминает Scala для демонстрации. Другие языки сделали другой выбор. Например, JavaScript использует обратные метки для интерполяции.

Какие из этих функций я бы больше всего хотел иметь в Java? Для меня это будет проверка синтаксиса во время компиляции. Прямо сейчас IDE могут сделать обоснованное предположение о том, что конкретная строка может быть, скажем, регулярным выражением, и дать предупреждение, если она искажена. Но было бы намного приятнее, если бы это была ошибка во время компиляции.

Конечно, это сложная проблема. Не существует механизма добавления подключаемых проверок во время компиляции, кроме обработки аннотаций. Можно предоставить аннотации, которые проверяют содержимое строки, и действительно, Checker Framework делает именно это. Но вы аннотируете переменные, а не строковые литералы, так что это не одно и то же.

Было бы также хорошо, если бы существовал стандартный способ интерполяции и форматирования. Прямо сейчас у нас есть String.format и String.format которые полезны, но несовместимы.

Вместо этого Java 12 дает нам необработанные / многострочные строки. Это тоже приятно.

Сырые строки

Рассмотрим, например, поиск периода с регулярным выражением. Регулярное выражение \. так как вы должны избежать периода в регулярном выражении. Так что в Java это Pattern.compile("\\.") . Чтобы соответствовать обратной косой Pattern.compile("\\\\") , это Pattern.compile("\\\\") . Это может стать действительно запутанным.

На самом деле, это настолько сбивает с толку, что автор JEP 326 ошибается — или, возможно, имеет тонкое чувство юмора. Примером автора является Pattern.compile("\\\"") для соответствия " . Конечно, вам не нужно избегать этого в регулярном выражении, так что Pattern.compile("\"") будет работать нормально. Это подтверждает тот факт, что все эти экранирование — беспорядок.

Средство простое. Заключите строку в кавычки `...` . Ничего внутри обратных Pattern.compile(`\.`) нужно экранировать: Pattern.compile(`\.`)

Но что, если строка содержит обратные пометки?

В Scala и Kotlin вы используете разделители """ , но возникает вопрос: а что если строка содержит """ ?

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

1
2
3
4
String s = `````. . .
. . .
. . .
. . .`````; // Five golden backticks 🙂

Все в строке берется именно так, как есть. Если это какой-то HTML или SQL или что-то, что вы разработали в другом месте, просто вставьте его.

На самом деле, «так, как есть», есть одно исключение. Все окончания строк нормализуются до \n , даже если в исходном файле используются окончания строк в стиле Windows \r\n .

Пара мух в мази

Стивен Колебурн отметил, что два кавычки можно спутать с пустой строкой. Если у вас есть что-то вроде

1
2
s = ``;
t = ``;

тогда это не устанавливает s и t в пустую строку, но s устанавливает в строку ";\nt = " .

Там есть хорошая загадка.

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

<

до> «`
оповещение («Привет, мир!»)

1
2
3
4
5
</pre>
You obviously can't add backticks at the start, so the best thing you can do is add a space or newline before the <code>```</code>. And the same holds for the end. Java requires that the ending delimiters exactly match the start. (In contrast, in Scala, you can write <code>"""Hello, "World""""</code>, and the compiler figures out that one of the terminal quotation marks belongs to the string.)
 
So, you can write:
<pre>String markdown = `````

оповещение («Привет, мир!»)
«`
««`.Strip ();

Вызов strip удаляет \n в начале и в конце. Или вы можете просто оставить новые строки на месте, если они не имеют значения.

(Метод strip является новым для Java 11. Он похож на trim , но он удаляет начальные и конечные пробелы Unicode, тогда как trim удаляет символы ≤ 32, что не одно и то же. В наши дни вы должны использовать strip , а не trim .)

Поддержка IDE

IntelliJ 2018.3 может преобразовывать строки с обратной косой чертой в необработанные строки при активации экспериментальных функций JDK 12. (Подробности см. В этом блоге ).
Пять струн
Я попытался преобразовать старомодную многострочную строку:

1
2
3
4
5
private static final String authorPublisherQuery = "SELECT Books.Price, Books.Title\n"
      + " FROM Books, BooksAuthors, Authors, Publishers\n"
      + " WHERE Authors.Author_Id = BooksAuthors.Author_Id AND BooksAuthors.ISBN = Books.ISBN\n"
      + " AND Books.Publisher_Id = Publishers.Publisher_Id AND Authors.Name = ?\n"
      + " AND Publishers.Name = ?\n";

Это не сработало, но нет никаких причин, почему это не могло бы произойти в будущем.

Управление отступами

Я предпочитаю выстраивать многострочные строки в крайнем левом столбце. Например,

1
2
3
4
5
6
7
public static void main(String[] args) {
      String myNameInABox = `
+-----+
| Cay |
+-----+`.strip();
      System.out.print(myNameInABox);
   }

Это выделяет многострочную строку из кода Java. И это дает вам много горизонтального пространства для всего, что вы вкладываете в строку.

Однако многие люди предпочитают стиль, в котором содержимое многострочной строки выровнено с кодом Java:

1
2
3
4
5
6
7
...
   String myNameInABox = `
                         +-----+
                         | Cay |
                         +-----+
                         `.align();
   System.out.print(myNameInABox);

Метод align (определенный в Java 12) удаляет общие префиксы пробелов, а также начальные и конечные пустые строки.

При таком подходе есть риск. Если используется смесь вкладок и пробелов, то каждая вкладка считается одним пробелом. Что-то может выглядеть выровненным с вами в вашей IDE, но не с методом align . Конечно, ваша IDE может предупредить вас о такой ситуации. В настоящее время IntelliJ 2018.3 этого не делает.

Дороги не взяты

Многие обсуждения новых функций происходят в списке рассылки «Amber Spec», который вы можете найти по адресу http://mail.openjdk.java.net/pipermail/amber-spec-observers/ , чтобы вы могли увидеть, какие альтернативы был рассмотрен.

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

А как насчет Unicode, экранированных внутри строк? Должен ли \u0060 быть \u0060 ? Здравый смысл восторжествовал, и было решено, что «сырье означает сырье».

Должны ли два обратных знака быть вне закона, потому что `` можно спутать с пустой строкой? Нет — было бы более важным иметь простое правило «любое количество обратных ударов с каждой стороны».

Как насчет новой строки после вводных кавычек? Были некоторые вопросы о том, следует ли их раздеть. Я все еще думаю, что немного грустно, что больше внимания не было уделено этой проблеме. Включение новой строки в начальный разделитель решило бы две проблемы: начальные обратные кавычки и выравнивание в крайнем левом столбце.

Я робко спросил, почему закрывающий разделитель не может быть «по крайней мере таким же количеством обратных кавычек, как и начальный разделитель» (аналогично Scala), поэтому необработанные строки могут заканчиваться обратными кавычками. К сожалению, я не получил ответа.

Удивительно, как много деталей входит в концептуально простую функцию, такую ​​как эта. Незначительные гниды в стороне, это очень желанный подарок, как раз к праздничному сезону.

Опубликовано на Java Code Geeks с разрешения Cat Horstmann, партнера нашей программы JCG . См. Оригинальную статью здесь: Пять «золотых галочек» Мнения, высказанные авторами Java Code Geeks, являются их собственными.