3

JNotify からの次のサンプルを考えると、implements を理解するのに少し苦労していますclass ListnerJNotifyListener のメソッド ( fileRenamedfileModifiedなど) を実装し、戻り値の型を void から Integer に変更するのは有効な Java 構文ですか?

JNotify の上に Clojure ライブラリを作成し、リスナーのメソッドのいずれかが返されるまで、Future に Clojure コード ブロックを配置したいと考えています。

  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);
    }
  }
4

3 に答える 3

3

いいえ。実装のシグネチャの戻り値の型は、実装するインターフェイスの戻り値の型と完全に一致するか、実装/拡張型と一致する必要があります。


コメントへの返信 (元の質問ではありません)

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

Java 6 コンパイラーは上記のコードを受け入れます。これは、戻り値がオーバーライド時に共変であることを意味するため、一致が正確である必要がないというコメンターは正しいです。

于 2013-04-22T18:30:28.213 に答える
3

OPからのコメントを明確にすることに応じて:

オブザーバー パターンの簡単な例を次に示します。

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実装して main に登録します。その後、イベントでメソッドが呼び出されます。FileChangeEventListenerFileChangeEventNotifyernotify

ここには、よくある落とし穴がいくつかあります。1 つは、この実装では を使用しないためsynchronized、イベントの発生時にクラスが登録されている場合、クラスが通知を見逃す可能性があることです。利点は、これがノンブロッキングであることです。したがって、ノンブロッキング コレクションとブロッキング コレクションのどちらが望ましいかは、あなた次第です。

また、登録済みのリスナーが後で登録解除されていることを確認する必要があります。そうしないと、リスナーが積み重なり、メモリリークが発生する可能性があります。

私はこれを単一のものとして実装FileChangeEvent しましたType-明らかに、親FileChangeEventクラスを持ち、それを型のサブクラスで拡張できます。繰り返しますが、それはあなたのニーズが何であるかによって異なります。

とにかく、これで始められるはずです。

于 2013-04-22T19:20:24.437 に答える
0

Clojure 側からは、future ではなく promise/deliver の使用を検討してください。その後、リスナーのメソッドは約束を果たすことができます。 http://clojuredocs.org/clojure_core/clojure.core/promise

これがライブラリの場合は、future や promise を使用するよりも、関数を渡す方がよい場合があります。これにより、ライブラリのユーザーは、別のスレッドの変更を導入するのではなく、独自のクロージャーを渡すことができます。

于 2013-04-22T20:08:23.473 に答える