2013-04-22 5 views
3

Учитывая следующий образец от JNotify, у меня есть небольшая проблема с пониманием. реализует. Является ли это синтаксисом Java для class Listner для реализации методов JNotifyListener - fileRenamed, fileModified и т. Д. - но изменить тип возвращаемого значения с void на Integer?Можно ли изменить пример, чтобы вернуть значение?

Я хочу написать библиотеку Clojure поверх JNotify и создать блок кода Clojure в будущем, пока не вернется один из методов Listener.

class Listener implements JNotifyListener { 
    public void fileRenamed(int wd, String rootPath, String oldName, 
     String newName) { 
     print("renamed " + rootPath + " : " + oldName + " -> " + newName); 
    } 
    public void fileModified(int wd, String rootPath, String name) { 
     print("modified " + rootPath + " : " + name); 
    } 
    public void fileDeleted(int wd, String rootPath, String name) { 
     print("deleted " + rootPath + " : " + name); 
    } 
    public void fileCreated(int wd, String rootPath, String name) { 
     print("created " + rootPath + " : " + name); 
    } 
    void print(String msg) { 
     System.err.println(msg); 
    } 
    } 
+1

Нет, вы не можете изменить сигнатуру метода таким образом, когда вы реализуете интерфейс, иначе люди не знают, чего ожидать от метода (вся точка интерфейса - это то, что вы знаете, что нужно передавать и что ожидать) – eldris

+1

Если интерфейс ожидает 'void', реализация должна возвращать' void'. –

+0

Вы можете использовать шаблон адаптера - оберните 'JNotifyListener' в другой' class', который возвращает значения. –

ответ

3

Количество Тип возвращаемого значения в сигнатуре реализации должен соответствовать типу возвращаемого значения интерфейса он реализует либо с точным соответствием или с реализации/простирающейся типа.


Ответ на комментарий (не оригинальный вопрос)

interface I { 
    A foo(); 
} 
class A {} 
class B extends A {} 
class C implements I { 
    public B foo() { return null; } 
} 

Java-6 Компилятора принимает выше код, который подразумевает возвращаемое значение ковариантно во время коррекции, так что комментаторы правильно, что совпадение не должно быть точным.

+1

Это верно только в случае примитивов и void. Для ссылочных типов возвращаемый тип фактически является ко-вариантом (при переопределении). –

+0

отредактирован, чтобы уточнить, что я говорил о подписи. Очевидно, что расширение или реализация могут быть возвращены в соответствие формальному типу. –

+0

, например. 'public C m()' переопределяет (или реализует) 'public P m()', если 'C' is-a' P'. –

3

В ответ на уточняющие комментарии от OP:

справа, вот краткий пример шаблона Observer:

public interface JNotifyListener { 

    void fileRenamed(int wd, String rootPath, String oldName, String newName); 

    void fileModified(int wd, String rootPath, String name); 

    void fileDeleted(int wd, String rootPath, String name); 

    void fileCreated(int wd, String rootPath, String name); 
} 

public enum Type { 

    RENAMED, 
    MODIFIED, 
    DELETED, 
    CREATED; 
} 

public class FileChangeEvent { 

    final Type type; 
    final int wd; 
    final String rootPath; 
    final String name; 
    final String newName; 

    public FileChangeEvent(Type type, int wd, String rootPath, String name, String newName) { 
     this.type = type; 
     this.wd = wd; 
     this.rootPath = rootPath; 
     this.name = name; 
     this.newName = newName; 
    } 

    public FileChangeEvent(Type type, int wd, String rootPath, String name) { 
     this(type, wd, rootPath, name, null); 
    } 

    public int getWd() { 
     return wd; 
    } 

    public String getRootPath() { 
     return rootPath; 
    } 

    public String getName() { 
     return name; 
    } 

    public String getNewName() { 
     return newName; 
    } 
} 

public interface FileChangeEventListener { 

    void notify(FileChangeEvent fileChangeEvent); 
} 

public class FileChangeEventNotifyer implements JNotifyListener { 

    final Collection<FileChangeEventListener> listeners = new ConcurrentLinkedQueue<FileChangeEventListener>(); 

    @Override 
    public void fileRenamed(int wd, String rootPath, String oldName, String newName) { 
     notifyAll(new FileChangeEvent(Type.RENAMED, wd, rootPath, oldName, newName)); 
    } 

    @Override 
    public void fileModified(int wd, String rootPath, String name) { 
     notifyAll(new FileChangeEvent(Type.MODIFIED, wd, rootPath, name)); 
    } 

    @Override 
    public void fileDeleted(int wd, String rootPath, String name) { 
     notifyAll(new FileChangeEvent(Type.DELETED, wd, rootPath, name)); 
    } 

    @Override 
    public void fileCreated(int wd, String rootPath, String name) { 
     notifyAll(new FileChangeEvent(Type.CREATED, wd, rootPath, name)); 
    } 

    private void notifyAll(final FileChangeEvent changeEvent) { 
     for (final FileChangeEventListener changeEventListener : listeners) { 
      changeEventListener.notify(changeEvent); 
     } 
    } 

    public void registerListener(final FileChangeEventListener eventListener) { 
     listeners.add(eventListener); 
    } 

    public void unregisterListener(final FileChangeEventListener eventListener) { 
     listeners.remove(eventListener); 
    } 
} 

Вы можете видеть, что class нужно только реализовать FileChangeEventListener и зарегистрировать свой интерес с основным FileChangeEventNotifyer. Затем он будет иметь свой метод notify, вызванный с событием.

Здесь есть пара общих подводных камней. Одна из них заключается в том, что эта реализация не использует synchronized, поэтому класс может пропустить уведомление, если оно регистрируется по мере того, как происходит событие. Преимущество заключается в том, что это неблокирование. Поэтому вам решать, желательно ли иметь неблокирующую коллекцию или блокировку.

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

Я реализовал это как единый FileChangeEvent с Type - очевидно, вы могли бы иметь родительский класс FileChangeEvent, а затем продлить его подклассов типов. Опять же, это зависит от ваших потребностей.

В любом случае, это должно вас начать.

+0

Хороший пример, bmorris. К окту, класс Guava 'ListenableFuture' также стоит посмотреть, прежде чем вы будете слишком инвестированы в ваниль' Future' –

0

Со стороны Clojure рассмотрите возможность использования обещаний/доставки, а не будущего. Тогда методы в Слушателе могут выполнить обещание. http://clojuredocs.org/clojure_core/clojure.core/promise

Если это для библиотеки, возможно, лучше передать функцию, а не использовать будущее или обещание. Это позволит пользователю библиотеки проходить в своем закрытии, вместо того, чтобы вводить другое изменение потока.

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