Что происходит, когда я добавляю char и String в Java? - PullRequest
5 голосов
/ 01 февраля 2020

Я знаю, что могу сделать что-то подобное в Java:

String foo = 'a' + "bee";

Если я напечатаю foo на консоли, я получу abee. Что на самом деле здесь происходит под одеялом? Я предполагаю, что 'a' повышается до String, но я не уверен, какие правила регулируют этот тип конверсии. Насколько я могу судить, это не случай автобокса, поскольку автобокс отображается из примитивного типа в класс-оболочку (например, int -> Integer). String считается оболочкой для char?

Одна вещь, которая делает это немного более интересным, заключается в том, что, если я делаю что-то вроде String test = 'a' + 'b';, я получаю ошибку компиляции. Я понимаю, что это связано с тем, что символы добавляются как целые числа при их добавлении, хотя разумно ожидать, что я получу что-то вроде "ab" в test с учетом поведения при добавлении char и String.

Ответы [ 2 ]

4 голосов
/ 03 февраля 2020

Вы точно назвали причину, по которой использование оператора + для объединения строк может рассматриваться как историческая ошибка проектирования. Предоставление встроенного оператора конкатенации не является неправильным, но это не должен быть оператор плюс.

Помимо путаницы в различном поведении, например, для 'a'+'b' и ""+'a'+'b', оператор плюс обычно ожидается коммутативный, т. е. a + b имеет тот же результат, что и b + a, что не относится к конкатенации строк. Кроме того, приоритет оператора может привести к неожиданностям.

Поведение точно определено (JLS §15.18.1) :

15.18.1. Оператор конкатенации строк +

Если только одно выражение операнда имеет тип String, то преобразование строки ( §5.1.11 ) выполняется для другого операнда для получения строки во время выполнения .

Результатом объединения строк является ссылка на объект String, который является объединением двух строк операндов. Символы левого операнда предшествуют символам правого операнда во вновь созданной строке.

Это определение ссылается на §5.1.11 :

5.1.11. Преобразование строки

Любой тип может быть преобразован в тип String с помощью преобразование строки .

Значение x примитивного типа T сначала преобразуется в ссылочное значение, как если бы оно передавалось в качестве аргумента соответствующему выражению создания экземпляра класса ( §15.9 ):

  • Если T равно boolean, то используйте new Boolean(x).

  • Если T равно char, тогда используйте new Character(x).

  • Если T равно byte, short или int, затем используйте new Integer(x).

  • Если T равно long, тогда используйте new Long(x).

  • Если T равно float, используйте new Float(x).

  • Если T равно double, используйте new Double(x).

Это эталонное значение затем преобразуется в тип String путем преобразования строки.

Теперь необходимо учитывать только эталонные значения:

  • Если ссылка null, она преобразуется в строку "null" (четыре символа ASCII n, u, l, l).

  • В противном случае преобразование выполняется как при вызове метода toString ссылочного объекта без аргументов; но если результат вызова метода toString равен null, тогда вместо него используется строка "null".

(форматирование спецификации действительно "null", а не "null")

Таким образом, поведение String foo = 'a' + "bee"; определено как как если бы вы написали String foo = new Character('a').toString() + "bee";

Но процитированный §15.18.1 продолжается с:

Объект String создается вновь ( §12.5 ), если только Выражение является константным выражением ( §15.28 ).

Реализация может выбрать выполнение преобразования и объединения за один шаг, чтобы избежать создания, а затем отбрасывания промежуточного объекта String. Чтобы повысить производительность многократной конкатенации строк, компилятор Java может использовать класс StringBuffer или аналогичный метод для уменьшения числа промежуточных String объектов, которые создаются путем вычисления выражения.

Для примитивных типов реализация также может оптимизировать создание объекта-обертки путем преобразования непосредственно из примитивного типа в строку.

Так что для вашего конкретного c пример, 'a' + "bee", фактическое поведение

String foo = 'a' + "bee";

будет

String foo = "abee";

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

Если один из операндов не является константой времени компиляции, как

char c = 'a';
String foo = c + "bee";

Оптимизированный вариант, используемый большинством, если не всеми компиляторами от Java 5 до Java 8 (включительно), это

char c = 'a';
String foo = new StringBuilder().append(c).append("bee").toString();

См. Также этот ответ . Начиная с Java 9, будет использоваться другой подход .

Результирующее поведение всегда будет таким, как указано.

3 голосов
/ 01 февраля 2020

Это особенность оператора + в JVM. Если хотя бы один из его операндов равен String, то второй также будет преобразован в String.

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

int i = 1;
int j = 1;
System.out.println("Sum of two ints: " + i + j);

Это приведет к Sum of two ints: 11, поскольку i и j конвертируются в строку, а затем используется конкатенация строк.

Но если вы используете следующий код, вы получите сумму

int i = 1;
int j = 1;
System.out.println("Sum of two ints: " + (i + j));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...