質問
Javaジェネリックをかなりよく理解していると思いましたが、java.lang.Enumで次のことに遭遇しました。
class Enum<E extends Enum<E>>
誰かがこの型パラメータの解釈方法を説明できますか?同様のタイプのパラメーターを使用できる他の例を提供することに対するボーナスポイント。
解決
enumの型引数は、それ自体が同じ型引数を持つenumから派生する必要があることを意味します。これはどのように起こりますか?型引数を新しい型自体にすることにより。したがって、StatusCodeという列挙型がある場合、それは次のようになります。
public class StatusCode extends Enum<StatusCode>
制約を確認すると、Enum<StatusCode>
-E=StatusCode
になります。確認しましょう:E
はEnum<E>
を拡張しますか?はい!大丈夫です。
これのポイントは何であるかを自問するかもしれません:)まあ、それはEnumのAPIがそれ自身を参照できることを意味します-例えば、Comparable<E>
はEnum
を実装すると言うことができます。基本クラスは比較を行うことができます(列挙型の場合)が、正しい種類の列挙型のみを比較することを確認できます。 (編集:まあ、ほぼ-下部の編集を参照してください。)
ProtocolBuffersのC#ポートで似たようなものを使用しました。 <!> quot; messages <!> quot;があります。 (不変)および<!> quot; builders <!> quot; (可変、メッセージの構築に使用)-タイプのペアとして提供されます。関連するインターフェイスは次のとおりです。
public interface IBuilder<TMessage, TBuilder>
where TMessage : IMessage<TMessage, TBuilder>
where TBuilder : IBuilder<TMessage, TBuilder>
public interface IMessage<TMessage, TBuilder>
where TMessage : IMessage<TMessage, TBuilder>
where TBuilder : IBuilder<TMessage, TBuilder>
これは、メッセージから適切なビルダーを取得できる(たとえば、メッセージのコピーを取得していくつかのビットを変更する)ことができ、ビルダーから構築が完了したときに適切なメッセージを取得できることを意味します。ただし、APIのユーザーは実際にこれを気にする必要はありません。恐ろしく複雑であり、その場所に到達するまでに数回の反復が必要です。
EDIT:これは、型引数を使用する奇数型を作成することを妨げるものではないことに注意してください。目的は、間違っているケースから保護するのではなく、正しいケースで利益をもたらすことです。
したがって、Second
が処理されなかった場合<!> quot;特に<!> quot;とにかくJavaでは、コメントに記載されているように、次のタイプを作成できます。
public class First extends Enum<First> {}
public class Second extends Enum<First> {}
Comparable<First>
はComparable<Second>
...ではなくFirst
を実装しますが、<=>自体は問題ありません。
他のヒント
以下は、本 Java Generics and Collections の説明の修正版です。
Enum
が宣言されています
enum Season { WINTER, SPRING, SUMMER, FALL }
クラスに展開されます
final class Season extends ...
where ...
は、何らかの方法でパラメータ化されたEnumの基本クラスです。働こう
それがどうあるべきかを。さて、Season
の要件の1つは、Comparable<Season>
を実装することです。必要になります
Season extends ... implements Comparable<Season>
これを機能させるためにEnum<Season>
に使用できるものは何ですか? <=>のパラメーター化である必要があるため、唯一の選択肢は<=>であるため、次のことが可能になります。
Season extends Enum<Season>
Enum<Season> implements Comparable<Season>
したがって、<=>は<=>などの型でパラメーター化されます。 <=>からの要約と <=>のパラメーターは
を満たす任意のタイプであることがわかります E extends Enum<E>
Maurice Naftalin(共著者、Java Generics and Collections)
これは、単純な例と、サブクラスの連鎖メソッド呼び出しを実装するために使用できる手法によって説明できます。以下の例では、setName
はNode
を返すため、チェーンはCity
に対して機能しません:
class Node {
String name;
Node setName(String name) {
this.name = name;
return this;
}
}
class City extends Node {
int square;
City setSquare(int square) {
this.square = square;
return this;
}
}
public static void main(String[] args) {
City city = new City()
.setName("LA")
.setSquare(100); // won't compile, setName() returns Node
}
したがって、<=>が正しい型を返すように、ジェネリック宣言でサブクラスを参照できます。
abstract class Node<SELF extends Node<SELF>>{
String name;
SELF setName(String name) {
this.name = name;
return self();
}
protected abstract SELF self();
}
class City extends Node<City> {
int square;
City setSquare(int square) {
this.square = square;
return self();
}
@Override
protected City self() {
return this;
}
public static void main(String[] args) {
City city = new City()
.setName("LA")
.setSquare(100); // ok!
}
}
それが何を意味するのか疑問に思っているのはあなただけではありません。 Chaotic Javaブログを参照してください。
<!>#8220;クラスがこのクラスを拡張する場合、パラメーターEを渡す必要があります。パラメーターE <!>#8217; sの境界は、同じパラメーターE <!>でこのクラスを拡張するクラス用です。 #8221;。
この投稿では、これらの「再帰ジェネリック型」の問題を完全に明らかにしました。 この特定の構造が必要な別のケースを追加したかっただけです。
一般的なグラフに一般的なノードがあるとします:
public abstract class Node<T extends Node<T>>
{
public void addNeighbor(T);
public void addNeighbors(Collection<? extends T> nodes);
public Collection<T> getNeighbor();
}
その後、特殊なタイプのグラフを作成できます:
public class City extends Node<City>
{
public void addNeighbor(City){...}
public void addNeighbors(Collection<? extends City> nodes){...}
public Collection<City> getNeighbor(){...}
}
Enum
ソースコードを見ると、次のようになっています。
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
}
まず最初に、E extends Enum<E>
はどういう意味ですか?これは、型パラメーターがEnumから拡張されたものであり、生の型でパラメーター化されていないことを意味します(それ自体でパラメーター化されます)。
これは、列挙型がある場合に関連します
public enum MyEnum {
THING1,
THING2;
}
私が正しく知っていれば、これは翻訳されています
public final class MyEnum extends Enum<MyEnum> {
public static final MyEnum THING1 = new MyEnum();
public static final MyEnum THING2 = new MyEnum();
}
つまり、これはMyEnumが次のメソッドを受け取ることを意味します。
public final int compareTo(MyEnum o) {
Enum<?> other = (Enum<?>)o;
Enum<MyEnum> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
さらに重要なのは、
@SuppressWarnings("unchecked")
public final Class<MyEnum> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<MyEnum>)clazz : (Class<MyEnum>)zuper;
}
これにより、getDeclaringClass()
が適切なClass<T>
オブジェクトにキャストされます。
より明確な例は、私がで回答した例です。この質問では、ジェネリックバウンドを指定する場合、この構造を避けることはできません。