Я знаю, что это распространенный вопрос, и я прочитал несколько ответов, но все они очень субъективны, и я пытаюсь получить ответ на наиболее распространенную практику (т. е. что использовать большую часть времени).
Я работаю на C#.net и переписываю приложение на Java (версия 21). В C# публичное свойство с методами получения и установки функционально совпадает с частным полем с открытыми методами получения и установки (пример C# ниже). Get и set неявно используются при ссылке на свойство. Таким образом, на практике в 99% случаев я использую общедоступное свойство с методами получения и установки, поскольку это дает мне дополнительное преимущество инкапсуляции и не добавляет многословия в код.
Теперь с Java я пытаюсь решить, когда использовать геттеры и сеттеры. Если бы я писал код так же, как на C#, большинство свойств моего Java-объекта были бы частными полями с общедоступными методами получения и установки. Это делает код очень многословным (пример 1 ниже), и я не уверен, что это необходимо/обычно для простых объектов, таких как ответы json (сопоставленные Джексоном) или сущностей.
Итак, с учетом всего сказанного, на практике это наиболее стандартный способ обработки простых свойств, которые просто получают и устанавливают поле в Java (без манипуляций с данными). Например, если бы я увидел корпоративное приложение, какой был бы наиболее стандартный способ обработки примеров 1 и примера 2 ниже. Основываясь на моих исследованиях, я склоняюсь к примеру 1 (сохраняет инкапсуляцию), но я не хочу использовать геттеры и сеттеры повсюду, если на самом деле более распространенной практикой является просто использование общедоступных полей с простыми свойствами. Дайте мне знать, что вы думаете.
С# получить/установить:
namespace SpeedRunAppImport.Model.Entity
{
public class GameEntity
{
public string Name { get; set; }
// Same thing as above
private string _Name;
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
}
}
}
}
Сущность игры:
@Entity
@Table(name = "tbl_game")
public class Game {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String code;
private String abbreviation;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getAbbreviation() {
return abbreviation;
}
public void setAbbreviation(String abbreviation) {
this.abbreviation = abbreviation;
}
}
GameResponse (ответ JSON, сопоставленный Джексоном):
public class GameResponse
{
private String id;
private String name;
private String abbreviation;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAbbreviation() {
return abbreviation;
}
public void setAbbreviation(String abbreviation) {
this.abbreviation = abbreviation;
}
}
Пример 1 (с использованием геттеров/сеттеров):
public void SaveGames(List<GameResponse> games)
{
_logger.info("Started SaveGames: {@count}", games.size());
var existingGamesVWs = _gameRepo.GetGamesByCode(games.stream().map(x -> x.getId()).toList());
var gameEntities = games.stream().map(i -> { var game = new Game();
var existingGame = existingGamesVWs.stream().filter(x -> x.code == i.getId()).findFirst().orElse(null);
game.setId(existingGame != null ? existingGame.getId() : null);
game.setName(i.getName());
game.setCode(i.getId());
game.setAbbr(i.getAbbreviation());
return game;
}).toArray(Game[]::new);
_logger.info(Integer.toString(existingGamesVWs.size()));
}
Пример 2 с использованием полей (предположим, общедоступные поля в объектах выше):
public void SaveGames(List<GameResponse> games)
{
_logger.info("Started SaveGames: {@count}", games.size());
var existingGamesVWs = _gameRepo.GetGamesByCode(games.stream().map(x -> x.id).toList());
var gameEntities = games.stream().map(i -> { var game = new Game();
var existingGame = existingGamesVWs.stream().filter(x -> x.code == i.id).findFirst().orElse(null);
game.id = existingGame != null ? existingGame.id : null;
game.name = i.name;
game.code = i.id;
game.abbr = i.abbreviation;
return game;
}).toArray(Game[]::new);
}
Ну, люди всегда хотят видеть задействованные объекты, код, по моему мнению, довольно читабелен. Если бы я просто привел последние два примера, люди бы спрашивали объекты, лол. Также хотел проиллюстрировать, что при использовании метода получения/установки они будут такими же, как везде, буквально везде, где я получаю или устанавливаю.
… вы спрашиваете о геттерах/сеттерах и публичных членах, когда манипуляции с данными не требуются 🤷♂️ YMMV, но кажется, что для демонстрации того, что вы спрашиваете, требуется очень мало (если вообще что-то). Я бы также сказал, что это уже до смерти обсуждалось в мире.
Да, я читал статьи и там действительно много предположений. Используйте записи, не используйте записи из-за неизменности. Используйте lombok, подождите, не используйте lombok, потому что когда-то он был привязан к определенным IDE (я использую код Visual Studio) и может вызвать дополнительную путаницу. Если это простые свойства неизменяемых объектов, просто используйте поля, подождите, не используйте поля, они теряют инкапсуляцию. Мне нужен был простой и краткий ответ «это то, что делает большинство людей», о многих методах написания кода можно спорить до смерти, я ищу, какой стандартный шаблон будет в 2024 году для этой проблемы.
Как вы обнаружили, это зависит от мнения и открыто для обсуждения. Делайте то, что подходит для вашей конкретной ситуации и экосистемы.
Правильно, вопрос в том, что чаще всего используют. Это не мнение, а скорее то, что является наиболее распространенным подходом на практике. Если для всего используются геттеры и сеттеры, то хорошо, я просто хотел знать, что наиболее распространено, поскольку у меня нет доступа к корпоративному Java-коду, чтобы увидеть.
YMMV, но существует масса Java-кода с открытым исходным кодом. Я полагаю, вы могли бы использовать простой анализатор, чтобы количественно определить, какой шаблон является «наиболее распространенным», если это так важно.
Да, просматривая некоторые примеры на github, я вижу частое использование геттеров/сеттеров, поэтому склоняюсь в этом направлении. Спасибо за отзыв.
Я не знаю, как вы думаете, что, задав тот же вопрос еще раз, вы получите менее субъективные ответы. Вам следует хотя бы дать ссылку на некоторые ответы, которые вы прочитали, чтобы мы точнее знали, чего вы не хотите.
Что ж, когда вопрос сформулирован «что является наиболее стандартным / распространенным», можно подумать, что люди дадут вам такой ответ. И, как вы видите ниже, ответ был дан. Вопрос совершенно актуальный, я не ищу интернет-дебатов, я ищу реальный ответ, спасибо.
«Это делает код очень многословным…» — для этого и нужен javadoc. Фундаментальная концепция объектно-ориентированной разработки заключается в том, что когда вам нужно получить краткое описание возможностей класса, вы должны смотреть на документацию (контракт), а не на внутреннюю реализацию. Создаваемая C# документация неуклюжа и неуклюжа (по крайней мере, так было в последний раз), но javadoc оптимизирован и организован.




Самый распространенный подход или соглашение в Java — использовать геттеры и сеттеры, чтобы соблюдать принцип инкапсуляции и упростить макетирование/тестирование классов, как и в любом другом языке. Некоторые языки долгое время делали это проще и менее многословным, хорошими примерами этого являются C# и Kotlin.
Однако Java обычно предпочитает стабильность синтаксическому сахару, не говоря уже о том, что не так уж много лет назад цикл выпуска был намного длиннее, и для того, чтобы эти улучшения синтаксиса стали доступны, требовалось больше времени.
Несколько способов добиться этого:
public class Example {
private final String value;
public Example(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
}
@Getter
@Setter
// or @Data, which adds more functionality (e.g. equals, hashCode, toString)
@AllArgsConstructor
public class Example {
private final String value;
}
record Example(String value) {}
В конце концов, выбор зависит от предпочтений, но в идеале следует соблюдать принцип инкапсуляции и атрибут качества тестируемости.
Понятно, спасибо за четкий и краткий ответ. Я просмотрел записи, но из-за их неизменяемости не думаю, что их можно использовать большую часть времени. Как вы сказали для ответов JSON (вероятно, будете использовать) или, может быть, для сущностей БД, я мог бы их использовать. Но часто я извлекаю объект из БД, обновляю его свойство и отправляю обратно в БД для сохранения, поэтому с неизменностью может возникнуть проблема. Звучит как вариант 1 или 2, Ломбок сначала отключил меня, потому что это плагин IDE, и я надеялся, что мой код останется независимым. Похоже, что вариант 1 лучше всего, еще раз спасибо.
Пожалуйста. Это верный момент в отношении записей, хотя даже в этом случае общепринятой практикой сохранения объектов неизменяемыми и потокобезопасными является просто создание еще одной записи на основе оригинала с необходимыми изменениями, отдающими предпочтение безопасности над производительностью. Опять же, классы данных Котлина упрощают это, например example.copy(value = "other"). Java не предлагает ничего подобного (во всяком случае, изначально).
Да, хороший момент, я вижу, что это довольно распространенная практика с сущностями. Просто это был небольшой дизайнерский шок от C#, где вы используете свойство, являющееся общедоступным, и особо о нем не думаете. Вероятно, одно из самых больших отличий дизайна, которое нужно ко всему привыкнуть, составляет почти 1:1 (даже с потоком вместо linq и т. д.). Так что пока мне просто придется привыкнуть к геттерам/сеттерам :). Еще раз спасибо.
Я не совсем понимаю, зачем в качестве примера требуется столько кода. В конце концов это мнение. Геттеры и сеттеры являются нормой, если вы не используете записи.