Ведение журнала по методам интерфейса по умолчанию
Приветствуйте всех гуру Java!
Так как Java8 мы можем иметь реализации по умолчанию в интерфейсах (yay!).
Однако проблема возникает, когда вы хотите использовать метод по умолчанию.
У меня такое чувство, что неразумно вызывать .getLogger() каждый раз, когда я хочу что-то записывать по умолчанию.
Да, можно определить статическую переменную в интерфейсе, но это не является хорошей практикой для интерфейсов в любом случае +, он предоставляет журнал (должен быть общедоступным).
Решение, которое у меня есть на данный момент:
interface WithTimeout<Action> {
default void onTimeout(Action timedOutAction) {
LogHolder.LOGGER.info("Action {} time out ignored.", timedOutAction);
}
static final class LogHolder {
private static final Logger LOGGER = getLogger(WithTimeout.class);
}
}
LogHolder по-прежнему отображается всем, что на самом деле не имеет никакого смысла, поскольку оно не предоставляет никаких методов и должно быть внутренним для интерфейса.
Знает ли кто-нибудь о лучшем решении?:)
EDIT: я использую SLF4J, поддерживаемый Logback
Ответы
Ответ 1
Если вы не хотите публиковать класс LogHolder
, не делайте его членом класса interface
. Нет никакой пользы в том, чтобы сделать его классом-членом, вы даже не сохраните ввод текста, поскольку вам все равно нужно квалифицировать доступ к полю с именем класса-владельца, независимо от того, является ли он классом-членом или классом в одном пакете:
public interface WithTimeout<Action> {
default void onTimeout(Action timedOutAction) {
LogHolder.LOGGER.info("Action {} time out ignored.", timedOutAction);
}
}
final class LogHolder { // not public
static final Logger LOGGER = getLogger(WithTimeout.class);
}
Ответ 2
Здесь вы идете.
Логгер закрыт для интерфейса. Никто кроме этого интерфейса и его методы по умолчанию не могут получить доступ к чему-либо внутри Test2. И ничто не может расширить класс Test2.
Никто не предлагает вам сделать это... но он работает!
И это отличный способ получить доступ к регистратору классов для основного интерфейса и, возможно, умный способ сделать что-то еще не совсем безумное.
Это действительно то же самое, что и LogHolder в вопросе OP, за исключением того, что класс Test2 - это все частные методы и конструктор private, а класс не помечен как static.
И в качестве дополнительного бонуса он сохраняет состояние, статически и для каждого экземпляра.
(Не делайте этого в реальной программе, пожалуйста!)
public class TestRunner {
public static void main(String[] args) {
Test test = new Test() {
};
test.sayHello("Jane");
System.out.println("Again");
test.sayHello("Bob");
}
}
public interface Test {
default void sayHello(String name) {
Logger log = Test2.log;
Test2 ref = Test2.getMine.apply(this);
int times = ref.getTimes();
for (int i = 0; i < times; i++) {
System.out.println(i + ": Hello " + name);
}
log.info("just said hello {} times :)",times);
}
final class Test2 {
private static final Logger log = LoggerFactory.getLogger(Test.class);
private static final Map lookup = new WeakHashMap();
private static final Function getMine = (obj) -> {
return lookup.computeIfAbsent(obj, (it) -> new Test2());
};
private int calls = 0;
private Test2() {
}
private void setCalls(int calls) {
this.calls = calls;
}
private int getCalls() {return calls;}
private int getTimes() {return ++calls;}
}
}
Ответ 3
Способ, которым я это обработал, заключался в том, чтобы перехватить и вернуть исключение из интерфейса, а затем обработать его в конкретном классе. Но есть риск пропустить исключение, если вы не будете осторожны.