6

質問のタイトルをお詫びします。これを簡単に言葉にすることができませんでした。

私はいくつかのコードでこれに出くわしました:

public class MyClass implements Message<MyClass> {...}

私はそれが何をするのか理解していますが、この方法で宣言されたクラスを見たことがありません。

私が見る欠点は、現在MyClassはメッセージであり、その主な目的とは無関係の実装されたメソッドを含める必要があることです。

私が見る利点の 1 つは (他の方法で作成する必要がある他のクラスの数を減らすこと以外に)、 のようなものComparableについて、MyClass自分自身を他のインスタンスと比較する方法を知っていることです。これにより、より簡潔なコードが作成されます。

これは良い習慣ですか?経験則はありますか?

4

4 に答える 4

8

これは、Java で、実装クラス自体を参照するメソッドとのインターフェースを持つ唯一の方法です。したがって、たとえば、バイナリ ツリー ノード インターフェイスを次のように記述できます。

interface TreeNode<N extends TreeNode<N>> {
  N getLeftChild();
  N getRightChild();
  void setLeftChild(N node);
  void setRightChild(N node);
}

そして、次のようなクラスがあります

class TreeNodeWithInt extends TreeNode<TreeNodeWithInt> {
  int value;
  TreeNodeWithInt leftChild;
  TreeNodeWithInt rightChild;
  public TreeNodeWithInt getLeftChild() { return leftChild; }
  public void setLeftChild(TreeNodeWithInt newLeft) { leftChild = newLeft; }
  ...
}

型パラメーターがなければ、次のNような安全でないコードを書く必要があります。

class TreeNodeWithInt extends TreeNode {
  int value;
  TreeNodeWithInt leftChild;
  TreeNodeWithInt rightChild;

  public void setLeftChild(TreeNode node) {
    // note the dangerous cast!
    leftChild = (TreeNodeWithInt) node;
  }
}

ここでの重要な問題は、メソッドの入力と戻りの型を記述するときに、インターフェイスを実装しているクラスの型をインターフェイスが参照できないことです。代わりに、「self」ジェネリック型パラメーターを含めます。これは、ジェネリックを多用するコードで比較的一般的なイディオムです。

あなたが正しく識別したように、オブジェクトを同じタイプの他のオブジェクトとしか比較できないためComparable、具体的に頻繁に使用されます。実際、はwhereを取るように指定されています。これは、少なくともtype の他の値に匹敵することを意味しますが、おそらく他の値にも匹敵します。Collections.sortList<T>T extends Comparable<? super T>TT

(最後に、ご想像のとおり、これはこの動作を実現する唯一の方法であるため、「良い」または「悪い」プラクティスではありません。とはいえ、この手法の目標は、警告なしでコンパイルされるが終了する可能性のあるメソッドを作成しないようにすることです。それClassCastException自体が良い練習です。)

于 2012-07-19T12:53:37.147 に答える
1

それは「良い習慣」でも「悪い習慣」でもありません。

本当の問題は、達成しようとしているものを正確にモデル化するかどうかです。場合によってはそうなりますが、そうでない場合もあります。

また、「メタパターン」の特定の使用法が望ましくない方法や無意味な方法になる場合は、それが適切な解決策ではないという強いケースがあります。

経験則はありますか?

特定のユースケースで機能しない場合は、そのユースケースで使用しないでください:-)

于 2012-07-19T12:50:12.123 に答える
0

私の例は C# で表現されていますが、Java に変換され、クラス、特にドメイン クラスの一般的なタスクを実行するための便利なイディオムとして理にかなっています。とはいえ、それは良い習慣でも悪い習慣でもありません。

class Product :  IEquatable<Product>, ICloneable<Product>, IComparable<Product>

またはJavaで

class Product implements IEquatable<Product>, ICloneable<Product>, IComparable<Product>

この例では、他の製品との同等性をチェックする方法、クローンを作成する方法 (おそらく一般的な再利用可能な方法で)、製品自体を比較する方法を知っている製品を定義できます。

私はコード生成で上記のようなものをよく使用し、可能な限り多くのジェネリックをルート レベルに配置し、より具体的なものをリーフ レベルで拡張します。

于 2012-07-19T13:02:33.753 に答える
0

良い質問です。Stephen C が述べたように、これは良い習慣でも悪い習慣でもありません。実際、あなたの説明はあまり正確ではありません。これは、それ自体を実装するクラスの場合ではなく、実装したいパラメーター化されたインターフェースのパラメーターであるとクラスが言っている場合です。

public class MyClass implements Message<MyClass> {...}

これは事実上、Message が「パラメーター化された」インターフェースであることを意味します。つまり、インターフェース自体がパラメーターを受け入れます。この場合、パラメーターは、パラメーター化されたクラスを実装している同じクラスになります。しかし、それは別のものかもしれません。これを想像してください:

public class UserDAO implements DAO<User> {...}
于 2012-07-19T13:02:57.233 に答える