次のように、ジェネリック型を使用して、メソッドの引数が同じ型であることを確認したいと思います。
public static <T> void x(T a, T b)
このメソッドに渡される2つの引数(aとb)は、常に同じタイプである必要があると思います。しかし、驚いたことに、どの引数が渡されても、TがObjectに消去されたかのように、任意の型(プリミティブも含む)の引数をメソッドxに渡すことができました。
これまでに見つけた唯一の回避策は、次のような「extends」を使用することでした。
public static <T, U extends T> void x(T a, U b)
しかし、私はそれと一緒に暮らすことができますが、それは私が望んでいたものではありません。
ジェネリック型を使用してメソッドのすべての引数の型を強制する方法はありますか?
私があなたの質問を正しく理解しているなら、あなたはこれが欲しいです:
x(10, "x");
コンパイル時に失敗します。今これを行うことを検討してください:
Integer i = 10;
String s = "x";
Object o1 = i;
Object o2 = s;
x(o1, o2);
この場合、それらは両方ともオブジェクトであり、同じタイプです。私は本当にあなたが望むものを強制する方法はないと思います-引数をObjectにキャストするとき、警告/エラーなしに2つの異なる型でそれを呼び出すことが常に可能です。
次のように使用して、使用するタイプを指定できます。
ClassName.<Type>x(obj1, obj2);
そして、それはおそらくそれを行う唯一の方法です。
私が正しく理解している場合、それを行う1つの方法は、 T のタイプを明示的に指定して、コンパイラにそのタイプを推測させるのではなく異なるタイプの2つのオブジェクトが引数として渡される場合の最も直接的なスーパークラスこのようなものを例にとります:
public class Test {
public static void main(String[] args) {
Test.x(5.0, 5); // This works since type is inferred to be Number
Test.<Integer>x(5, 5); // This works since type is stated to be Integer
Test.<Integer>x(5.0, 5); // This doesn't; type is stated to be Integer and Double is passed in
}
public static <T> void x(T a, T b) {
}
}
そもそもなぜこれが問題となるのかは、私にとっては漠然としています。代わりに、型システムが役立つ方法について何か誤解しているのではないかと思います。
<T> void x(T a, T b)
で何ができるでしょうか?まあ、全部ではありません。 x
の本体の内部では、T
はObject
と同じであるため、toString
でa
を呼び出すようなことしかできません。それらを印刷するにはb
。
実際には実用的ではありません理由a
とb
は同じタイプでなければなりません。共通するタイプがあり、そのタイプはObject
またはそのサブタイプであるというだけです。実際、<T> void x(T a, T b)
が実際に汎用である必要がある明確な理由はありません。
a
とb
の実際のタイプが何であるかを気にしません。これは、とにかくそれらを使用できなかったためです。a
はb
メソッドなので、呼び出しサイトはx
とvoid
の実際のタイプが何であるかを気にしません。したがって、ブラックホールです。<T> List<T> Arrays.asList(T...)
のような結果が得られるメソッドの方が一般的です。
// This will cause a compile error because
// the type inferred must be compatible
// with the return assignment.
List<Integer> r = Arrays.asList(1, 1.0);
または限界:
// We don't care what the actual types of
// a and b are, just that we can call bar()
// on them.
// Note: this method does not need to be generic.
<T extends Foo> void x(T a, T b) {
a.bar();
a.bar();
}
または、ある種の関係を主張する境界:
// We don't care what the actual types of
// a and b are, just that we can compare
// them to each other.
<T extends Comparable<T>> T max(T a, T b) {
return (a.compareTo(b) < 0) ? b : a;
}
メソッドを呼び出すときに、typeパラメーターを明示的に指定できます。例えば:
<String>x("hello", "world");
ただし、しない type-parameterを明示的に指定し、Javaの型推論機能のみに依存している場合は、ジェネリックスだけでなく、一般的にはできないと思います。
メソッドパラメータのタイプは具体的なタイプではありませんが、の適用可能なタイプのセット(これもを示すものです) setは、たとえば、final
クラスの場合、1つのタイプのみで構成できます。
たとえば、このメソッドは次のとおりです。
public void x(Something a) { }
メソッドを示します。このパラメータは、タイプのセットからのタイプである必要があります。これは、Something
(つまり、Something
およびそのすべてのサブタイプ)。
同じことがジェネリックにも当てはまります。
おそらく、一般的な方法で一般的なメソッドを呼び出していないため、x(Object a, Object b)
の呼び出しのように扱われます。この例では:
public class Test {
static <T> void x(T a, T b) {
}
public static void main(String[] args) {
x(1, 2); // compiles
Test.<String>x(1, 2); // does not compile
Test.<String>x("a", "b"); // compiles
}
}
Xへの最初の呼び出しは一般的に行われないため、コンパイルされます。 2番目の呼び出しはT
をString
に等しくするため、1
および2
はStrings
ではありません。 3番目の呼び出しは、Strings
を適切に渡すため、コンパイルされます。
これは私のために働いた
public static <T> void x(T a, T b, Class<T> cls) {
}
今これはコンパイルされます
public static void main(String[] args) throws Exception {
x(1, 2, Integer.class);
}
そしてこれはしません
public static void main(String[] args) throws Exception {
x(1, "", Integer.class);
}