Заставить String.format(%s, arg) отображать аргументы с нулевым значением иначе, чем null

Рассмотрим пользовательскую toString() реализацию bean-компонента:

@Override
public String toString() {
    String.format("this is %s", this.someField);
}

Это дает this is null, если someField равно нулю.

Есть ли способ переопределить строковое представление по умолчанию null аргументов с нулевым значением другим текстом, т. е. ? без явного вызова replaceAll(...) в методе toString?

Примечание. Компонент наследуется от суперкласса, который может реализовать Formattable (http://docs.oracle.com/javase/7/docs/api/java/util/Formattable.html), но я просто не понимаю, как это сделать.

EDIT: Фрагмент слишком упрощен для примера, но я не ищу решения для троичных операторов someField==null ? "?" : someField, потому что:

  • может быть (потенциально) очень много полей, задействованных в toString(), поэтому проверка всех полей слишком громоздка и неудобна.
  • другие люди, над которыми у меня мало контроля (если таковые имеются), пишут свои собственные подклассы.
  • если метод вызывается и возвращает null, это подразумевает либо двойной вызов метода, либо объявление локальной переменной.

Вернее, можно ли что-нибудь сделать, используя интерфейс Formattable или какой-нибудь пользовательский Formatter (кстати, final)?


person VH-NZZ    schedule 05.12.2013    source источник
comment
я бы сделал String.format("this is %s", this.someField==null?"?":this.someField);   -  person Arnaud Denoyelle    schedule 05.12.2013
comment
Или с гуавой: String.format("this is %s", Objects.firstNotNull(this.someField, "?"));   -  person Arnaud Denoyelle    schedule 05.12.2013
comment
Я всегда с любовью думаю о firstNotNull, особенно. аналогичные реализации некоторых диалектов SQL, но Guava слишком тяжелая зависимость и больше относится к зависимостям класса Utils, чем к pojo, imo.   -  person VH-NZZ    schedule 05.12.2013
comment
@ VH-NZZ Есть несколько отличных решений. Вы уверены, что не можете выбрать один для принятия?   -  person Kim Kern    schedule 30.01.2019
comment
@KimKern Большинство, если не все, так или иначе решают проблему и в конечном итоге дают результат, совместимый с вопросом, хотя и за некоторые деньги. При этом я объективно не могу утверждать, что могу выбрать один ответ как действительно лучший, поэтому: нет, на данном этапе, боюсь, я не могу.   -  person VH-NZZ    schedule 31.01.2019


Ответы (10)


С java 8 теперь вы можете использовать для этого необязательный класс:

import static java.util.Optional.ofNullable;
...
String myString = null;
System.out.printf("myString: %s",
    ofNullable(myString).orElse("Not found")
);
person Dmitry Klochkov    schedule 02.02.2016
comment
Чтобы применить также «пустые» значения, его можно расширить с помощью «filter(s -> !s.isEmpty())» следующим образом: System.out.printf(myString: %s, ofNullable(myString).filter( s -> !s.isEmpty()).orElse(Не найдено) - person burak; 01.10.2020

Для решения Java 7, для которого не требуются внешние библиотеки:

String.format("this is %s", Objects.toString(this.someField, "?"));
person Klitos Kyriacou    schedule 05.01.2017

На мой взгляд, лучшее решение — использовать метод объектов Guava, firstNonNull. Следующий метод гарантирует, что вы напечатаете пустую строку, если someField когда-либо имеет значение null.

String.format("this is %s", MoreObjects.firstNonNull(this.someField, ""));

Документация по Guava.

person JavaThunderFromDownunder    schedule 14.05.2014
comment
String.format("this is %s", StringUtils.defaultIfBlank(this.someField, "")); сделает свое дело, если Apache StringUtils окажется в вашем пути к классам. - person dvtoever; 18.12.2015

Немного поздно по этому поводу, но это может быть довольно чистое решение: сначала создайте свой собственный метод форматирования...

private static String NULL_STRING = "?";

private static String formatNull(String str, Object... args){
    for(int i = 0; i < args.length; i++){
        if(args[i] == null){
            args[i] = NULL_STRING;
        }
    }

    return String.format(str, args);
}

Тогда используйте его как хотите...

@Test
public void TestNullFormat(){
    Object ob1 = null;
    Object ob2 = "a test";

    String str = formatNull("this is %s", ob1);
    assertEquals("this is ?", str);

    str = formatNull("this is %s", ob2);
    assertEquals("this is a test", str);
}

Это устраняет необходимость в нескольких трудночитаемых тернарных операторах.

person JM Lord    schedule 10.04.2014
comment
... но подразумевает зависимость от вашего метода formatNull. - person VH-NZZ; 10.04.2014
comment
Конечно! Если только упомянутый выше случай не относится к какому-то классу выходных стратегий, в котором метод может быть определен частным образом в самой стратегии. Тогда не было бы проблемы зависимости. Если он используется во многих местах приложения, то да, его нужно поместить в какой-то класс библиотеки/утилиты. - person JM Lord; 12.04.2014

Если вы не хотите использовать replaceAll(), вы можете назначить текст по умолчанию (строка) для someField.

Но если через какое-то время это может снова назначить null. Таким образом, вы можете использовать проверку для этого случая

 this.someField == null ? "defaultText" : this.someField
person Ruchira Gayan Ranaweera    schedule 05.12.2013

Чтобы избежать повторения тернарного оператора, вы можете обернуть его более читаемым методом, который проверит, является ли ваш объект null, и вернет какое-то значение по умолчанию, если оно верно, как

static <T> T changeNull(T arg, T defaultValue) {
    return arg == null ? defaultValue : arg;
}

Применение

String field = null;
Integer id = null;
System.out.printf("field is %s %n", changeNull(field, ""));
System.out.printf("id is %d %n", changeNull(id, -1));
System.out.printf("id is %s %n", changeNull(field, ""));

выход:

field is  
id is -1 
id is  
person Pshemo    schedule 05.12.2013

Из java 7 вы можете использовать Objects.toString(Object o, String nullDefault).

Применительно к вашему примеру: String.format("this is %s", Objects.toString(this.someField, "?"));

person Bob Rivers    schedule 03.05.2017

Вы могли бы просто сделать

String.format("this is %s", (this.someField==null?"DEFAULT":this.someField));
person joey.enfield    schedule 05.12.2013
comment
Опять же, это использование тернарного оператора. Спасибо, в любом случае. - person VH-NZZ; 11.01.2014

Чтобы сохранить исходное значение someField (в случае, если null является допустимым значением), вы можете использовать тернарный оператор.

String.format("This is %s", (this.someField == null ? "unknown" : this.someField));
person Chris Forrence    schedule 05.12.2013

public static String format(String format, Object... args){
    for (int i=0;i<args.length;i++){
        if (args[i]==null) args[i]="";
    }
    return String.format(format,args);
}

затем используйте метод, хорошо

person eebb88    schedule 13.04.2017