Кейс: маленький фрагмент Java: String s = null; if (s.equals("test")) ... — объясните проблему и предложите безопасные шаблоны работы со строками и null‑безопасность в разных языках
Проблема - В строке `String s = null; if (s.equals("test")) ...` вызов `s.equals(...)` выполняется на `null` и приведёт к `NullPointerException`. Нельзя вызывать методы на `null`. Безопасные шаблоны и практики по языкам Java - Вызывать метод на литерале/константе: `if ("test".equals(s)) { ... }` — безопасно, потому что литерал не null. - Использовать утилиты: `if (Objects.equals(s, "test")) { ... }` (введено в Java 777) — null‑safe. `if (StringUtils.equals(s, "test")) { ... }` (Apache Commons Lang) — тоже null‑safe. - Явная проверка: `if (s != null && s.equals("test")) { ... }` - Optional (Java 888): `if (Optional.ofNullable(s).filter("test"::equals).isPresent()) { ... }` - Рекомендации: по возможности избегать возвращения `null` (возвращайте пустые строки или `Optional`), использовать `Objects.requireNonNull` для жёсткого fail‑fast. Kotlin - Оператор безопасного вызова и сравнение с `true`: `if (s?.equals("test") == true) { ... }` - Или просто использовать оператор `==` (structural equality) — он null‑safe: `if (s == "test") { ... }` - Предпочтительно использовать nullable типы `String?` и безопасные вызовы/эльвинг‑операторы. C# - Статический метод `String.Equals` (null‑safe): `if (String.Equals(s, "test")) { ... }` - С указанием сравнения: `if (String.Equals(s, "test", StringComparison.OrdinalIgnoreCase)) { ... }` - Оператор безопасного вызова: `if (s?.Equals("test") == true) { ... }` - Рекомендуется использовать nullable‑типы и `??` для дефолта: `if ((s ?? "") == "test")`. JavaScript / TypeScript - Прямое сравнение не бросит: `if (s === "test") { ... }` — вернёт `false`, если `s === null`. - Для методов: `if ((s ?? "").toLowerCase() === "test") { ... }` или optional chaining: `if (s?.toLowerCase() === "test") { ... }` - Не вызывать методы на `null`/`undefined` без проверки. Python - Сравнение с `None` безопасно: `if s == "test": ...` - Для методов: `if (s or "").lower() == "test": ...` или `if s is not None and s.lower() == "test": ...` C / C++ - C: перед `strcmp` обязательно проверять указатель: `if (s && strcmp(s, "test") == 0) { ... }` - C++: предпочитать `std::string` (не `nullptr`), либо использовать `std::optional` и проверять `has_value()`. Rust - Использовать `Option`: `if matches!(s.as_deref(), Some("test")) { ... }` или `if s.as_deref() == Some("test") { ... }` - Не иметь `null` — использовать `Option` обязательно. Общие рекомендации - Вызывать методы на константах/литералах, а не на переменных (например: `"test".equals(s)`). - По возможности избегать `null` — использовать Option/Optional/nullable‑aware типы. - Централизовать проверки/адаптеры (helper функции `safeEquals(a,b)`). - Fail‑fast: валидировать входные данные (requireNonNull) или задавать безопасные значения по умолчанию. - Для сравнения с учётом регистра всегда указывать явную стратегию сравнения (locale/ordinal). Если нужно, приведу краткие код‑примеры для конкретной версии языка.
- В строке `String s = null; if (s.equals("test")) ...` вызов `s.equals(...)` выполняется на `null` и приведёт к `NullPointerException`. Нельзя вызывать методы на `null`.
Безопасные шаблоны и практики по языкам
Java
- Вызывать метод на литерале/константе:
`if ("test".equals(s)) { ... }` — безопасно, потому что литерал не null.
- Использовать утилиты:
`if (Objects.equals(s, "test")) { ... }` (введено в Java 777) — null‑safe.
`if (StringUtils.equals(s, "test")) { ... }` (Apache Commons Lang) — тоже null‑safe.
- Явная проверка:
`if (s != null && s.equals("test")) { ... }`
- Optional (Java 888):
`if (Optional.ofNullable(s).filter("test"::equals).isPresent()) { ... }`
- Рекомендации: по возможности избегать возвращения `null` (возвращайте пустые строки или `Optional`), использовать `Objects.requireNonNull` для жёсткого fail‑fast.
Kotlin
- Оператор безопасного вызова и сравнение с `true`:
`if (s?.equals("test") == true) { ... }`
- Или просто использовать оператор `==` (structural equality) — он null‑safe:
`if (s == "test") { ... }`
- Предпочтительно использовать nullable типы `String?` и безопасные вызовы/эльвинг‑операторы.
C#
- Статический метод `String.Equals` (null‑safe):
`if (String.Equals(s, "test")) { ... }`
- С указанием сравнения:
`if (String.Equals(s, "test", StringComparison.OrdinalIgnoreCase)) { ... }`
- Оператор безопасного вызова:
`if (s?.Equals("test") == true) { ... }`
- Рекомендуется использовать nullable‑типы и `??` для дефолта: `if ((s ?? "") == "test")`.
JavaScript / TypeScript
- Прямое сравнение не бросит: `if (s === "test") { ... }` — вернёт `false`, если `s === null`.
- Для методов: `if ((s ?? "").toLowerCase() === "test") { ... }` или optional chaining: `if (s?.toLowerCase() === "test") { ... }`
- Не вызывать методы на `null`/`undefined` без проверки.
Python
- Сравнение с `None` безопасно: `if s == "test": ...`
- Для методов: `if (s or "").lower() == "test": ...` или `if s is not None and s.lower() == "test": ...`
C / C++
- C: перед `strcmp` обязательно проверять указатель: `if (s && strcmp(s, "test") == 0) { ... }`
- C++: предпочитать `std::string` (не `nullptr`), либо использовать `std::optional` и проверять `has_value()`.
Rust
- Использовать `Option`:
`if matches!(s.as_deref(), Some("test")) { ... }`
или
`if s.as_deref() == Some("test") { ... }`
- Не иметь `null` — использовать `Option` обязательно.
Общие рекомендации
- Вызывать методы на константах/литералах, а не на переменных (например: `"test".equals(s)`).
- По возможности избегать `null` — использовать Option/Optional/nullable‑aware типы.
- Централизовать проверки/адаптеры (helper функции `safeEquals(a,b)`).
- Fail‑fast: валидировать входные данные (requireNonNull) или задавать безопасные значения по умолчанию.
- Для сравнения с учётом регистра всегда указывать явную стратегию сравнения (locale/ordinal).
Если нужно, приведу краткие код‑примеры для конкретной версии языка.