2015-02-25 3 views
10

Салют всем гуру 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

+0

«У меня есть ощущение, что это не разумно называть .getLogger() каждый раз, когда я хочу что-то войти метод по умолчанию «. Вы выполнили измерения производительности, чтобы поддержать это чувство? Если нет, не оптимизируйте производительность, если вам это не нужно. – Ray

+1

Публикация публичного класса LogHolder или публичного журнала Logger LOGGER на мой взгляд не имеет большого значения. – assylias

+1

Я не вижу причин для _yay! _ –

ответ

7

Если вы не хотите подвергать класс 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); 
} 
+0

Да, но это не ограничивает доступ других членов одного и того же пакета к самому журналу. Может быть, я слишком пурист. Достойный программист вряд ли сможет использовать регистратор другого класса и не позволит ему пройти обзор кода, я думаю. –

+0

Поскольку у '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ', ' Но даже если бы был способ сделать 'LogHolder'« приватным »классом, который не мешал другим классам обращаться к' getLogger (WithTimeout.class) самим себе, чтобы получить один и тот же экземпляр журнала, поэтому это не стоит усилий. Полезно переместить класс из «интерфейса», чтобы избежать загрязнения API с помощью артефактов реализации, но регистраторы не должны быть релевантными для безопасности. – Holger

+0

да, вы правы - спасибо за обсуждение :) –

0

Здесь вы идете.

Регистратор частный для интерфейса. Никто кроме этого интерфейса и его методы по умолчанию не могут получить доступ к чему-либо внутри Test2. И ничто не может расширить класс Test2.

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

Это действительно то же самое, что и LogHolder в вопросе OP, за исключением того, что класс Test2 - это все частные методы и собственный конструктор, а класс не помечен как 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;} 
    } 
}

+1

Вы в основном изменили решение, которое уже было при открытии этого вопроса, добавив много чего, ОП не просил. Класс 'Test2' все еще имеет видимость« public », и он по-прежнему является« статическим »независимо от того, объявляете ли вы его явно или нет. Добавление конструктора 'private' является небольшим улучшением, но не меняет того факта, что класс в целом виден в API интерфейса' Test'. Нет никаких причин, чтобы привести обескураженный экземпляр к сопоставлению данных с ним, поскольку ОП никогда не просил связать данные с экземплярами. Разве это действительно стоило раскопать вопрос> 1½ года? – Holger

+0

Я просто демонстрирую, что * * может быть сделано, нравится вам это или нет. Помимо видимости таинственного вспомогательного класса LogHolder, в решении нет ничего плохого. Я думаю, что многие люди оценят выбор, который у них есть. Необходимость иметь регистратор в методе по умолчанию реальна, и это (и OP) предоставляет способ сделать это. Это просто гарантирует, что LogHolder не будет расширяемым или инстантабельным. –

Смежные вопросы