В java выкатывают так называемые строчные шаблоны. Возможность внедрять выражения внутрь строки и интерполировать.
Идея примерно такая: tpl."Hello, \{name}! Today is \{LocalDate.now()}."
компилятором преобразуется примерно вот в такое tpl.process(List.of("Hello, ", "! Today is ", "."), List.of(name, LocalDate.now()))
Собственно всё. Т.е. строчка разделяется на текстовые фрагменты и захваченные значения. Дальше дело техники — можно определять свои шаблоны и любую хитрую обработку фрагментов и значений. В качестве примера приводится SQL (полный код тут):
PreparedStatement ps = DB."SELECT * FROM Person p WHERE p.last_name = \{name}";
ResultSet rs = ps.executeQuery();
Тут кастомный процессор DB создаёт из строки объект типа PreparedStatement с "?" тексте запроса и подставляет соответствующие параметры.
Вот такие макросы. Простые до безобразия, но, имхо, довольно мощные, покрывают довольно много сценариев.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>В java выкатывают так называемые строчные шаблоны. Возможность внедрять выражения внутрь строки и интерполировать.
В смысле в Java интерполяции строк (string interpolation) до этого не было что ли
Она уже вроде везде давно (десятки лет) везде есть — шарп, питон, жаваскрипт, да куча их
Здравствуйте, bnk, Вы писали:
bnk>·>В java выкатывают так называемые строчные шаблоны. Возможность внедрять выражения внутрь строки и интерполировать. bnk>В смысле в Java интерполяции строк (string interpolation) до этого не было что ли bnk>Она уже вроде везде давно (десятки лет) везде есть — шарп, питон, жаваскрипт, да куча их
Так она везде не работает же нормально, прочитай что я написал хоть.
В шарпах-жабаскриптах у тебя интерполяция строки вшита в язык даёт на выходе строку с тупой подстановкой. В сабже — это расширяемая фича.
Например, в шарпе $"SELECT * FROM Person p WHERE p.last_name = '{name}'" даёт тебе строчку с sql-injection, в java — безопасный PreparedStatement.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>В шарпах-жабаскриптах у тебя интерполяция строки вшита в язык даёт на выходе строку с тупой подстановкой. В сабже — это расширяемая фича. https://learn.microsoft.com/en-us/dotnet/api/system.icustomformatter?view=net-7.0
·>Например, в шарпе $"SELECT * FROM Person p WHERE p.last_name = '{name}'" даёт тебе строчку с sql-injection, в java — безопасный PreparedStatement.
var provider = new MyCustomSqlFormatProvider();
FormattableString sql = $"SELECT * FROM SomeTable WHERE Age = {age} and Name = {name}";
var safeSqlString = sql.ToString(provider);
Здравствуйте, ·, Вы писали:
·>Здравствуйте, bnk, Вы писали:
bnk>>·>В java выкатывают так называемые строчные шаблоны. Возможность внедрять выражения внутрь строки и интерполировать. bnk>>В смысле в Java интерполяции строк (string interpolation) до этого не было что ли bnk>>Она уже вроде везде давно (десятки лет) везде есть — шарп, питон, жаваскрипт, да куча их ·>Так она везде не работает же нормально, прочитай что я написал хоть.
·>В шарпах-жабаскриптах у тебя интерполяция строки вшита в язык даёт на выходе строку с тупой подстановкой. В сабже — это расширяемая фича. ·>Например, в шарпе $"SELECT * FROM Person p WHERE p.last_name = '{name}'" даёт тебе строчку с sql-injection, в java — безопасный PreparedStatement.
S>var provider = new MyCustomSqlFormatProvider();
S>FormattableString sql = $"SELECT * FROM SomeTable WHERE Age = {age} and Name = {name}";
S>var safeSqlString = sql.ToString(provider);
S>
Ещё раз. Это даст строку. Неясно как из этого получить объект произвольного типа. Представь себе, что name это бинарный поток, например. В base64 перекладывать будешь или как?
Вот как реализация DB выглядит в примере:
public PreparedStatement process(StringTemplate st) throws SQLException {
// 1. Replace StringTemplate placeholders with PreparedStatement placeholders
String query = String.join("?", st.fragments());
// 2. Create the PreparedStatement on the connection
PreparedStatement ps = conn.prepareStatement(query);
// 3. Set parameters of the PreparedStatement
int index = 1;
for (Object value : st.values()) {
switch (value) {
case Integer i -> ps.setInt(index++, i);
case Float f -> ps.setFloat(index++, f);
case Double d -> ps.setDouble(index++, d);
case Boolean b -> ps.setBoolean(index++, b);
default -> ps.setString(index++, String.valueOf(value));
}
}
return ps;
}
Принципиальная разница в том, что шарпы-скрипты могут создавать только текстовую строку, а сабж — произвольный объект.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
M>STR is a public static final field that is automatically imported into every Java source file.
M>Это стандартный подход? Выглядит как какая-то магия.
Только стат-переменная STR. Примерно как автоимпорт всего "java.lang.*"
STR."xxx" это наиболее близкий аналог шапрного $"xxx", вот и включили по дефолту, видимо.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Здравствуйте, samius, Вы писали:
·>Ещё раз. Это даст строку. Неясно как из этого получить объект произвольного типа. Представь себе, что name это бинарный поток, например. В base64 перекладывать будешь или как?
Да уж, и забрать ADO команду с параметрами напрямую не выйдет. Только сериализованную...
·>Вот как реализация DB выглядит в примере:
·>Принципиальная разница в том, что шарпы-скрипты могут создавать только текстовую строку, а сабж — произвольный объект.
Здравствуйте, ·, Вы писали:
·>Вот такие макросы. Простые до безобразия, но, имхо, довольно мощные, покрывают довольно много сценариев.
Не хватает опциональных спецификаторов для форматирования.
Если взять тот же пример с SQL, то в JDBC в общем случае требуется указывать тип аргумента.
Т.е. правильный код должен выглядеть примерно так:
PreparedStatement ps = db."insert into person (name) values (\{name:VARCHAR2})";
ps.executeUpdate();
Этого, к сожалению, нет. Конечно можно вынести их за фигурную скобку и парсить строку, но это уже не совсем то... Лучшее, что можно придумать это обёртку:
PreparedStatement ps = db."insert into person (name) values (\{arg(name, VARCHAR2)})";
ps.executeUpdate();
Также они были бы полезны для обычного форматирования строк: fmt"n=\{n:02x}" вместо format("n=%02x", n).
Здравствуйте, vsb, Вы писали:
vsb>·>Вот такие макросы. Простые до безобразия, но, имхо, довольно мощные, покрывают довольно много сценариев. vsb>Не хватает опциональных спецификаторов для форматирования.
Ты не понял, наверное. Внутри \{} находится обычное java-выражение, т.е. произвольный код и у выражения есть java-тип. В отличие от всяких шарпов — типы сохраняются, а не "всё — строки".
vsb>Если взять тот же пример с SQL, то в JDBC в общем случае требуется указывать тип аргумента.
Не надо. Есть же тип у выражения.
int age = 18;
String name = "vasya";
db."insert...\{age} \{name}"
Тебе внутрь шаблона придёт List.of(18, "vasya") для каждого элемена можно сделать instanceof и соответсвующие маппинги типов.
vsb>Т.е. правильный код должен выглядеть примерно так:
Нет, или я не понял что именно ты имеешь в виду, что делать с этим VARCHAR2?
vsb>Этого, к сожалению, нет. Конечно можно вынести их за фигурную скобку и парсить строку, но это уже не совсем то... Лучшее, что можно придумать это обёртку:
Это если тебе надо типы java->sql преобразовывать только для каждого аргумента по-разному.
vsb>Также они были бы полезны для обычного форматирования строк: fmt"n=\{n:02x}" вместо format("n=%02x", n).
Это уже реализовали в стандартном FMT, погляди как на страничке jep. Просто наоборот записывается: FMT."n=%02x\{n}".
vsb>Не понял, правда, при чём тут макросы.
Ну обычно во всяких немерлях такое делали макросами которое, конечно, навороченнее, но гораздо сложнее. А тут всё просто.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
vsb>>·>Вот такие макросы. Простые до безобразия, но, имхо, довольно мощные, покрывают довольно много сценариев. vsb>>Не хватает опциональных спецификаторов для форматирования. ·>Ты не понял, наверное. Внутри \{} находится обычное java-выражение, т.е. произвольный код и у выражения есть java-тип.
Я всё понял, я читал этот JEP.
.> В отличие от всяких шарпов — типы сохраняются, а не "всё — строки".
Сохраняется тип значения, а не тип выражения.
vsb>>Если взять тот же пример с SQL, то в JDBC в общем случае требуется указывать тип аргумента. ·>Не надо. Есть же тип у выражения. ·>
·>int age = 18;
·>String name = "vasya";
·>db."insert...\{age} \{name}"
·>
·>Тебе внутрь шаблона придёт List.of(18, "vasya") для каждого элемена можно сделать instanceof и соответсвующие маппинги типов.
А теперь то же самое для случая, когда name == null.
vsb>>Т.е. правильный код должен выглядеть примерно так: ·>Нет, или я не понял что именно ты имеешь в виду, что делать с этим VARCHAR2?
Передавать его в PreparedStatement.setObject.
vsb>>Также они были бы полезны для обычного форматирования строк: fmt"n=\{n:02x}" вместо format("n=%02x", n). ·>Это уже реализовали в стандартном FMT, погляди как на страничке jep. Просто наоборот записывается: FMT."n=%02x\{n}".
Ну как я и написал — парсить строку самому. Если это, к примеру, SQL, то сразу будет куча нюансов, чтобы не было совпадения синтаксиса с чем-нибудь. Не идеально. Опять же две функции — STR и FMT вместо одной универсальной.
Здравствуйте, vsb, Вы писали:
.>> В отличие от всяких шарпов — типы сохраняются, а не "всё — строки". vsb>Сохраняется тип значения, а не тип выражения.
Эээ.. ну да. Но тип значения даже точнее. Или что ты имеешь в виду?
vsb>>>Если взять тот же пример с SQL, то в JDBC в общем случае требуется указывать тип аргумента. vsb>·>Не надо. Есть же тип у выражения. vsb>·>
vsb>·>int age = 18;
vsb>·>String name = "vasya";
vsb>·>db."insert...\{age} \{name}"
vsb>·>
vsb>·>Тебе внутрь шаблона придёт List.of(18, "vasya") для каждого элемена можно сделать instanceof и соответсвующие маппинги типов.
vsb>А теперь то же самое для случая, когда name == null.
А, понял. А setObject разве не сработает с java.sql.Types.NULL?
Но суть даже не в этом. Всё равно я не вижу нужды в каком-то новом особом магическом синтаксисе \{name:VARCHAR2}. У нас же там java код и можно оборачивать как обычный код. Если тебе не нравится \{arg(name, VARCHAR2)} можно запилить например \{DB.VARCHAR2(name)}. Может буковок и чуть больше, но ведь это обычный java-код, который будет обычным образом авто-комплититься, рефакториться, навигироваться, етс.
vsb>>>Т.е. правильный код должен выглядеть примерно так: vsb>·>Нет, или я не понял что именно ты имеешь в виду, что делать с этим VARCHAR2? vsb>Передавать его в PreparedStatement.setObject.
Тут ведь как — это будет всё равно гораздо лучше, чем есть на сегодняшний день.
vsb>>>Также они были бы полезны для обычного форматирования строк: fmt"n=\{n:02x}" вместо format("n=%02x", n). vsb>·>Это уже реализовали в стандартном FMT, погляди как на страничке jep. Просто наоборот записывается: FMT."n=%02x\{n}". vsb>Ну как я и написал — парсить строку самому. Если это, к примеру, SQL, то сразу будет куча нюансов, чтобы не было совпадения синтаксиса с чем-нибудь. Не идеально.
А кто по-твоему должен парсить магию "\{name:VARCHAR2}"? Зато вот \{DB.VARCHAR2(name)} — парсит компилятор — как обычный ява-код.
vsb> Опять же две функции — STR и FMT вместо одной универсальной.
Так ведь у них разные предназначения. STR это замена "aa" + n + "bb", а FMT это форматтер со своим синтаксисом. print vs printf
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
.>>> В отличие от всяких шарпов — типы сохраняются, а не "всё — строки". vsb>>Сохраняется тип значения, а не тип выражения. ·>Эээ.. ну да. Но тип значения даже точнее. Или что ты имеешь в виду?
Я имею в виду, что в теории компилятор знает значение выражения `name` и мог бы его передавать как-нибудь. Кстати это позволило бы получить информацию про generic типы, если вдруг кому-то надо.
vsb>>>>Если взять тот же пример с SQL, то в JDBC в общем случае требуется указывать тип аргумента. vsb>>·>Не надо. Есть же тип у выражения. vsb>>·>
vsb>>·>int age = 18;
vsb>>·>String name = "vasya";
vsb>>·>db."insert...\{age} \{name}"
vsb>>·>
vsb>>·>Тебе внутрь шаблона придёт List.of(18, "vasya") для каждого элемена можно сделать instanceof и соответсвующие маппинги типов.
vsb>>А теперь то же самое для случая, когда name == null. ·>А, понял. А setObject разве не сработает с java.sql.Types.NULL?
В общем случае не сработает, драйверу нужен правильный тип. Если его не передавать, он его пытается извлечь из переданного значения, но для null так не получится. В конкретных драйверах для конкретных БД может и сработает. Зависит от БД и драйвера, но в общем случае тип нужен.
·>Но суть даже не в этом. Всё равно я не вижу нужды в каком-то новом особом магическом синтаксисе \{name:VARCHAR2}. У нас же там java код и можно оборачивать как обычный код. Если тебе не нравится \{arg(name, VARCHAR2)} можно запилить например \{DB.VARCHAR2(name)}. Может буковок и чуть больше, но ведь это обычный java-код, который будет обычным образом авто-комплититься, рефакториться, навигироваться, етс.
Суть фичи ведь в том, чтобы писать код, который компактней и понятней. Так-то можно и вообще без неё обходиться, как сейчас обходимся. А авто-комплититься и рефакториться будет любой синтаксис, не думаю, что для IDE есть разница, поддерживать только синтаксис \(expr) или ещё и \(expr: String).
vsb>>>>Т.е. правильный код должен выглядеть примерно так: vsb>>·>Нет, или я не понял что именно ты имеешь в виду, что делать с этим VARCHAR2? vsb>>Передавать его в PreparedStatement.setObject. ·>Тут ведь как — это будет всё равно гораздо лучше, чем есть на сегодняшний день.
Фича классная, тут я не спорю. Я на самом деле когда-то в котлине агитировал сделать такую фичу, но не прислушались. А тут — в жаве почти что мне хочется.
vsb>>Ну как я и написал — парсить строку самому. Если это, к примеру, SQL, то сразу будет куча нюансов, чтобы не было совпадения синтаксиса с чем-нибудь. Не идеально. ·>А кто по-твоему должен парсить магию "\{name:VARCHAR2}"?
Что-то парсит компилятор, то, что после ":" — в случае FMT передаётся как обычная строка, в случае SQL, конечно, хотелось бы какого-то дополнения от IDE, а не просто обычную строку (хотя и так подошло бы). Тут надо подумать, как сделать это так, чтобы оба варианта работали.
vsb>> Опять же две функции — STR и FMT вместо одной универсальной. ·>Так ведь у них разные предназначения. STR это замена "aa" + n + "bb", а FMT это форматтер со своим синтаксисом. print vs printf
А если мне надо и то и то? Сейчас это FMT"map[%s\(key)] = %08x\(value)". А могло бы быть STR"map[\(key)] = \(value:08x)".
Здравствуйте, ·, Вы писали:
·>Здравствуйте, samius, Вы писали:
S>>
S>>var provider = new MyCustomSqlFormatProvider();
S>>FormattableString sql = $"SELECT * FROM SomeTable WHERE Age = {age} and Name = {name}";
S>>var safeSqlString = sql.ToString(provider);
S>>
·>Ещё раз. Это даст строку. Неясно как из этого получить объект произвольного типа.
Строка — это просто вариант по умолчанию. В общем случае ты можешь написать свой InterpolatedStringHandler и заиметь полный аналог того, что в заглавном посте для джавы было написано.
DbDataReader reader = DB.ExecuteToReader($"SELECT * FROM Person p WHERE p.last_name = {name}"); // будет сгенерён sql c параметром
Здравствуйте, Jack128, Вы писали:
J> Строка — это просто вариант по умолчанию. В общем случае ты можешь написать свой InterpolatedStringHandler и заиметь полный того, что в заглавном посте для джавы было написано. J>
J> DbDataReader reader = DB.ExecuteToReader($"SELECT * FROM Person p WHERE p.last_name = {name}"); // будет сгенерён sql c параметром
J>
Да, похоже. Но как-то страшно выглядит... Магические атрибуты какие-то и куча магии в компиляторе. С другой стороны, наверное проще для оптимизатора кода.
Здравствуйте, vsb, Вы писали:
vsb> Я имею в виду, что в теории компилятор знает значение выражения `name` и мог бы его передавать как-нибудь. Кстати это позволило бы получить информацию про generic типы, если вдруг кому-то надо.
Это уже какая-то другая фича, непосредствевнно к сабжу не имеющая. Если такое и делать, то без привязки к сабжу.
vsb> vsb>>А теперь то же самое для случая, когда name == null. vsb> ·>А, понял. А setObject разве не сработает с java.sql.Types.NULL? vsb> В общем случае не сработает, драйверу нужен правильный тип. Если его не передавать, он его пытается извлечь из переданного значения, но для null так не получится. В конкретных драйверах для конкретных БД может и сработает. Зависит от БД и драйвера, но в общем случае тип нужен.
В смысле в общем случае надо обязательно делать rs.setNull(Types.VARCHAR2)? Тогда да, придётся как-то этот тип затаскивать со значением парама. А вообще ты на старую мозоль наступил — the billion dollar mistake.
vsb> Суть фичи ведь в том, чтобы писать код, который компактней и понятней. Так-то можно и вообще без неё обходиться, как сейчас обходимся. А авто-комплититься и рефакториться будет любой синтаксис, не думаю, что для IDE есть разница, поддерживать только синтаксис \(expr) или ещё и \(expr: String).
Разница в том, что expr — это универсальный java-expr. Его можно в переменную извлечь или в метод, например. Если же специально для сабжа вводить какой-то новый string-template-expr, то это будет костыль сбоку. Вон тут прислали ссылочку на шарповый аналог, так это по-моему хак на хаке. В *стандарте* самого языка прописаны всякие alignment/width и прочее. ИЧСХ твоё {name:VARCHAR2} туда никак не укладывается всё равно.
В сабже компилятор делает лишь тупое заворачивание в List.of и всё, остальное — STR/FMT/etc — библиотечный код.
vsb> Фича классная, тут я не спорю. Я на самом деле когда-то в котлине агитировал сделать такую фичу, но не прислушались. А тут — в жаве почти что мне хочется.
Мне пока тоже не нравится твоя идея со встроенным специальным синтаксисом для шаблонных выражений, да ещё и кастомизируемым.
vsb> ·>А кто по-твоему должен парсить магию "\{name:VARCHAR2}"? vsb> Что-то парсит компилятор, то, что после ":" — в случае FMT передаётся как обычная строка, в случае SQL, конечно, хотелось бы какого-то дополнения от IDE, а не просто обычную строку (хотя и так подошло бы). Тут надо подумать, как сделать это так, чтобы оба варианта работали.
Именно. Т.е. твои кастомные шаблоны в IDE из коробки неясно как будут работать. "Обычная строка" — ну нафиг, наелись, у нас же всё-таки строго типизируемый ЯП. А расширения типа "\{DB.VARCHAR2(name)}" ты можешь клепать как самый обычный java-код.
vsb> ·>Так ведь у них разные предназначения. STR это замена "aa" + n + "bb", а FMT это форматтер со своим синтаксисом. print vs printf vsb> А если мне надо и то и то? Сейчас это FMT"map[%s\(key)] = %08x\(value)". А могло бы быть STR"map[\(key)] = \(value:08x)".
Принципиальная разница в том, что "aa" + n + "bb" — это фича компилятора, часть JLS. А FMT — это просто библиотечная функция по мотивам сишного printf, часть java.text в JDK. И ты можешь сам клепать подобные MY_BETTER_FMT.
Здравствуйте, ·, Вы писали:
·>Здравствуйте, Jack128, Вы писали:
J>> Строка — это просто вариант по умолчанию. В общем случае ты можешь написать свой InterpolatedStringHandler и заиметь полный того, что в заглавном посте для джавы было написано. J>>
J>> DbDataReader reader = DB.ExecuteToReader($"SELECT * FROM Person p WHERE p.last_name = {name}"); // будет сгенерён sql c параметром
J>>
·>Да, похоже. Но как-то страшно выглядит... Магические атрибуты какие-то и куча магии в компиляторе. С другой стороны, наверное проще для оптимизатора кода.
Магия этого атрибута — ничто по сравнению с DllImportAttribute, но как то живём.
А так, да, там и производительность больше (сможет джава убрать вызовы List.of если они не нужны? например Logger.TRACE."Залогинился \{userName}", а логируем мы только ошибки )
Еще в хенлере используется обычная перегрузка, поэтому мы можем ограничить множество типов, допустимых в плейсхолерах. Например в SqlInterpolatedStringHandler можно пускать только числовые типы, строку, дату и byte[] , а на остальные типы компилятор будет ругаться.
Поэтому да, возможностей больше, общая спецификация сложнее.
Здравствуйте, ·, Вы писали: ·>Ты не понял, наверное. Внутри \{} находится обычное java-выражение, т.е. произвольный код и у выражения есть java-тип. В отличие от всяких шарпов — типы сохраняются
Вы недооцениваете шарп.
Там с типами всё в порядке. Ну, и в отличие от джавы, есть ещё и возможность откладывать выполнение, что полезно, в частности, для отладки. Интерполяция может вовсе не вызываться, если на то нет соответствующего условия.
Что во многих случаях позволяет много чего заоптимизировать.
Реализуется это всё довольно сложной магией, но в применении она весьма проста.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, ·, Вы писали:
·>Здравствуйте, Jack128, Вы писали:
J>> Строка — это просто вариант по умолчанию. В общем случае ты можешь написать свой InterpolatedStringHandler и заиметь полный того, что в заглавном посте для джавы было написано. J>>
J>> DbDataReader reader = DB.ExecuteToReader($"SELECT * FROM Person p WHERE p.last_name = {name}"); // будет сгенерён sql c параметром
J>>