Возможно ли в Java проверить, являются ли поля объектов нулевыми, а затем добавить значение по умолчанию ко всем этим атрибутам?
Вопрос
Мне нужно убедиться, что ни один атрибут объекта не равен null, и добавить значение по умолчанию в случае, если оно равно null.Есть ли какой-нибудь простой способ сделать это, или я должен сделать это вручную, проверяя каждый атрибут по его получателям и установщикам?
Решение
Вы можете использовать отражение, чтобы выполнить итерацию по полю объекта и задать их.Очевидно, вам потребуется какое-то сопоставление между типами или даже именами полей и обязательными значениями по умолчанию, но это можно довольно легко сделать в цикле.Например:
for (Field f : obj.getClass().getFields()) {
f.setAccessible(true);
if (f.get(obj) == null) {
f.set(obj, getDefaultValueForType(f.getType()));
}
}
[Обновление]
В современной Java вы можете использовать аннотации для установки значений по умолчанию для полей для каждого класса.Полная реализация может выглядеть следующим образом:
// DefaultString.java:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface DefaultString {
String value();
}
// DefaultInteger.java:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface DefaultInteger {
int value();
}
// DefaultPojo.java:
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public class DefaultPojo {
public void setDefaults() {
for (Field f : getClass().getFields()) {
f.setAccessible(true);
try {
if (f.get(this) == null) {
f.set(this, getDefaultValueFromAnnotation(f.getAnnotations()));
}
} catch (IllegalAccessException e) { // shouldn't happen because I used setAccessible
}
}
}
private Object getDefaultValueFromAnnotation(Annotation[] annotations) {
for (Annotation a : annotations) {
if (a instanceof DefaultString)
return ((DefaultString)a).value();
if (a instanceof DefaultInteger)
return ((DefaultInteger)a).value();
}
return null;
}
}
// Test Pojo
public class TestPojo extends DefaultPojo {
@DefaultString("Hello world!")
public String stringValue;
@DefaultInteger(42);
public int integerValue;
}
Затем значения по умолчанию для TestPojo
может быть установлен простым запуском test.setDetaults()
Другие советы
Вам нужно вручную отфильтровать входные данные по конструкторам и установщикам.Что ж...вы могли бы использовать reflection, но я бы не советовал этого.Частью работы конструкторов и установщиков является проверка входных данных.Это может включать в себя такие вещи, как:
public void setPrice(double price) {
if (price < 0.0d) {
throw new IllegalArgumentException("price cannot be negative " + price);
}
this.price = price;
}
и
public void setName(String name) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
}
Вы могли бы использовать функции-оболочки для фактической проверки и создания исключения.
Может быть, проверить Валидатор гибернации 4.0, эталонная реализация JSR 303:Валидация компонента.
Это пример аннотированного класса:
public class Address {
@NotNull
private String line1;
private String line2;
private String zip;
private String state;
@Length(max = 20)
@NotNull
private String country;
@Range(min = -2, max = 50, message = "Floor out of range")
public int floor;
...
}
Для ознакомления см. Начало работы с JSR 303 (проверка компонентов) – часть 1 и часть 2 или раздел "Начало работы" справочного руководства, которое является частью дистрибутива Hibernate Validator.
Неотражающим решением для Java 8, без использования серии if, было бы передавать все поля потоком и проверять на nullness:
return Stream.of(id, name).allMatch(Objects::isNull);
Это остается довольно простым в обслуживании, при этом избегается удар отражающего молотка.Это вернет значение true для нулевых атрибутов.
Вы можете создать функцию, которая возвращает логическое значение и проверяет каждый атрибут.Вы можете вызвать эту функцию, чтобы она выполнила эту работу за вас.
В качестве альтернативы вы можете инициализировать объект значениями по умолчанию.Таким образом, вам не нужно будет проводить какую-либо проверку.
У меня недостаточно контекста, чтобы дать вам правильный ответ, но я предложу вам создать свой код неизменяемый как можно больше.Использование public final
поля.Не более getters
или setters
:каждое поле должно быть определено с помощью constructor
.Ваш код короче, более читабелен и предотвращает написание кода с побочными эффектами.
Однако это не мешает вам передавать нулевые аргументы вашему конструктору...Вы все еще можете проверить каждый аргумент, как предложил @cletus, но я посоветую вам бросить IllegalArgumentException
вместо того , чтобы NullPointerException
это не дает никакого нового намека на то, что вы сделали.
Во всяком случае, это то, что я делаю столько, сколько могу, и это значительно улучшило мой код (читабельность, стабильность).Все в моей команде делают это, и мы очень довольны этим.Мы узнали это, когда попытались написать несколько erlang
код, в котором все является неизменяемым.
Надеюсь, это поможет.
Я попробовал это, и это работает без каких-либо проблем, чтобы проверить, пусто ли поле.Я частично ответил на ваш вопрос, поскольку лично не пытался добавлять значения по умолчанию к атрибутам
if(field.getText()!= null && !field.getText().isEmpty())
Надеюсь, это поможет
Это делается не для проверки наличия null, вместо этого это будет полезно при преобразовании существующего объекта в пустой объект (свежий объект).Я не знаю, актуально это или нет, но у меня было такое требование.
@SuppressWarnings({ "unchecked" })
static void emptyObject(Object obj)
{
Class c1 = obj.getClass();
Field[] fields = c1.getDeclaredFields();
for(Field field : fields)
{
try
{
if(field.getType().getCanonicalName() == "boolean")
{
field.set(obj, false);
}
else if(field.getType().getCanonicalName() == "char")
{
field.set(obj, '\u0000');
}
else if((field.getType().isPrimitive()))
{
field.set(obj, 0);
}
else
{
field.set(obj, null);
}
}
catch(Exception ex)
{
}
}
}