Javaでリフレクトされたメソッドの戻り値の型とパラメーターを検証する
-
03-07-2019 - |
質問
クロージャーがない場合、Javaに(プリミティブ)コールバック機能を提供する汎用Callbackオブジェクトがあります。 CallbackオブジェクトにはMethodが含まれ、Methodの同等のメソッドに委任する2つのアクセサーメソッドを介してメソッドのパラメーターと戻り値の型を返します。
指定されたコールバックが有効なメソッドを指していることを検証しようとしています。 Numberと互換性のある戻り型の割り当てと、Doubleと互換性のあるすべてのパラメーターが必要です。私の検証方法は次のようになります。
static public void checkFunctionSpec(Callback cbk) {
Class[] prms=cbk.getParmTypes();
Class ret =cbk.getReturnType();
if(!Number.class.isAssignableFrom(ret)) {
throw new IllegalArgumentException(
"A function callback must return a Number type " +
"(any Number object or numeric primitive) - function '" +
cbk + "' is not permitted");
}
for(Class prm: prms) {
if(!Double.class.isAssignableFrom(prm)) {
throw new IllegalArgumentException(
"A function callback must take parameters of " +
"assignment compatible with double " +
"(a Double or Float object or a double or float primitive) " +
"- function '" + cbk + "' is not permitted");
}
}
}
私が遭遇する問題は、これを試してみると、 Math.abs()、次のように戻り型の例外をスローしています:
java.lang.IllegalArgumentException:
A function callback must return a Number type (any Number object or numeric primitive)
- function 'public static double java.lang.Math.abs(double)' is not permitted
これは、(a)ラッパークラスを使用して反映されるプリミティブ、および(b)Double.TYPEがClass <!> lt; Double <! > gt;。
チェックを変更せずにこれを達成する方法を誰もが知っていますか:
if(!Number.class.isAssignableFrom(ret)
&& ret!=Double.TYPE
&& ret!=Float.TYPE
&& ret!=...) {
明確化
Method.invoke()を使用してメソッドdouble abs(double)
を呼び出すと、Object [] {Double}を渡し、Doubleを取得します。ただし、Double.TYPEはDoubleに割り当てできないため、検証は失敗したようです。これらのコールバックはすべて、invoke()によってNumberとして返される何らかの番号を返す必要があるため、提供されたメソッドがNumberまたは数値プリミティブのいずれかを返すことを検証しようとしています。
パラメータの検証も同様です。
つまり、リフレクションを使用する場合、parm型とreturn型のDoubleとdoubleは同一であり、これらを簡単に検証したいです。
編集:さらに明確にするために、invoke()が呼び出されたときにメソッドがNumber型のオブジェクトを返すことを検証したい(そこからobj.doubleValue()を呼び出して、必要なdoubleを取得できます)。
解決
Class.isAssignableFrom()のドキュメントをより詳しく見ると、プリミティブの型はそれ以外のクラスと一致しないことが明確に述べられています。したがって、戻り型のByte.TYPE、Double.TYPE、Float.TYPE、Integer.TYPE、Long.TYPE、およびShort.TYPEと等しい==を特に確認する必要があります。
他のヒント
なぜコンパイラに実行させないのですか?
public interface F<A, B> {
public B $(A a);
}
その後、F<Double, Double>
を期待するメソッドにF<? extends Number, ? extends Number>
を渡すことができます。
編集:
あなたは、任意の数の引数を持つ関数の型に単一のクラスを提供したいと言います。これは、Java型システムで実行できます 。概念的には、すべての関数には引数が1つしかありません。 2つの引数を持つ関数は、別の関数を返す関数と同等です。そのため、値が2つのdoubleをとる関数である変数を次に示します。
F<Double, F<Double, Double>> f;
2つのdoubleを特定の関数に渡すメソッドは次のとおりです。
public Double operate(F<Double, F<Double, Double>> f, double a, double b) {
return f.$(a).$(b);
}
または、<!> quot; cons <!> quot;を表す2つのサブクラスL<A extends L>
と、ターミネータタイプC<E, T extends L<T>>
:
N
を考えます
public abstract class L<A extends L<A>> {
private L() {}
private static final N nil = new N();
public static N nil() {
return nil;
}
public static final class N extends L<N> {
private N() {}
public <E> C<E, N> cons(final E e) {
return new C<E, L>(e, this);
}
}
public static final class C<E, L extends L<L>> extends L<C<E, L>> {
private E e;
private L l;
private C(final E e, final L l) {
this.e = e;
this.l = l;
}
public E head() {
return e;
}
public L tail() {
return l;
}
public <E> C<E, C<E, L>> cons(final E e) {
return new C<E, C<E, L>>(e, this);
}
}
}
このような場合、関数型をこのように実装できます:
public interface F<A extends L<A>, B> {
public B $(A args);
}
次のメソッドは、2つのDouble
引数を持つ関数(およびdouble
を返す)と、それを適用する2つのF
を必要とします:
public Double operate(F<C<Double, C<Double, N>>, Double> f, double a, double b) {
return f.$(N.nil().cons(b).cons(a));
}
head
インターフェースの実装では、tail
および<=>を使用してリストから引数を取得する必要があります。したがって、実際には、JavaでLISPを実装しています。 :)
と言ったら、 Functional Java をチェックしてください。リフレクションを使用するものもあるので、自分で書く必要はありません。
Math.abs()のパラメーターは、ダブルプリミティブです。プリミティブが<!> quot; assignment compatible <!> quot;であることの意味がよくわかりません。オブジェクトを使用して(リフレクションAPIが本質的に意味するのは<!> quot; <!> quot;のキャストです)。しかし、あなたが<!> quot;がDoubleコンストラクター<!> quot;に渡せるということなら、それは本質的にプリミティブなdouble(または文字列)です!!おそらく、あなたがする必要があることをもう少し明確にする必要がありますか?