7

ToIntFunction インターフェイスを記述した場合、次のように、それがプリミティブな int を返す単なる関数であるという事実をインターフェイスにエンコードしたいと思います。

@FunctionalInterface
public interface ToIntFunction<T> extends Function<T, Integer> {
    int applyAsInt(T value);

    @Override
    default Integer apply(T value) {
        return Integer.valueOf(applyAsInt(value));
    }
}

私が疑問に思っていたのは、Java 8 API 設計者がプリミティブな代替手段を関数から完全に分離しておくことを選択した説得力のある理由があるのでしょうか? 彼らがそうすることを検討し、それに反対したという証拠はありますか? Consumer (Function<T, Void> の可能性があります) や Supplier (Function<Void, T>) など、他の「特別な」機能インターフェイスの少なくとも一部についても、同様の質問があると思います。

私はこれのすべての影響について非常に深く徹底的に考えていないので、おそらく何かが欠けている.

ToIntFunction (およびその他のプリミティブな汎用関数インターフェイス) が Function とこの関係を持っていた場合、Function パラメーターが期待される場所でそれを使用できるようになります (思いつくのは、myFunction.compose(myIntFunction) の呼び出しなど、他の関数との構成です)。または、上記のような自動 (非) ボックス化の実装で十分な場合に、API にいくつかの特殊な関数を記述することを避けるため)。

これは、この質問と非常によく似ています: Java 8 の Predicate<T> が Function<T, Boolean> を拡張しないのはなぜですか。したがって、関数の単純なプリミティブ代替のこの場合の質問を再定式化しています。セマンティクスはなく、プリミティブとラップされた型だけであり、null ラップされたオブジェクトの可能性さえも排除されます。

4

2 に答える 2

16

JDK 8 でのインターフェースの急増は、 Java の小さな問題の 1 つである値型の欠如の産物です。

これは、ジェネリックでプリミティブ型を使用できないことを意味するため、ラッパー型を使用する必要があります。

つまり、これは不可能です。

Function<String, int> myFunction;

しかし、これは次のとおりです。

Function<String, Integer> myFunction;

これに関する問題は、ボックス化/ボックス化解除です。これは高価になり、プリミティブ値のラッパー オブジェクトを常に作成する必要があるため、プリミティブ データ型を扱うアルゴリズムの最適化が困難になる可能性があり、その逆も同様です。

これは、JDK 8 で や などのインターフェイスが急増している理由を説明してFunctionIntFunctionます。後者はプリミティブ型を引数として使用します。

これはLambda メーリング リストのある時点で議論され、専門家グループがこれに苦労していたことが明らかになりました。

ラムダ プロジェクトの仕様リーダーである Brian Goetz は、次のように書いています。

より一般的には、特殊化されたプリミティブ ストリーム (たとえば、IntStream) を持つことの背後にある哲学には、厄介なトレードオフが伴います。一方で、それは多くの醜いコードの重複インターフェイスの汚染などです。他方では、ボックス化された opでのあらゆる種類の算術演算はひどいものであり、int を削減するためのストーリーがないのはひどいことです。ですから、私たちは厳しい状況にあり、それを悪化させないように努めています。

悪化させないための秘訣 #1 は、8 つのプリミティブ型すべてを実行しているわけではないということです。int、long、および double を実行しています。他のすべてはこれらによってシミュレートできます。おそらく、int も取り除くことができますが、ほとんどの Java 開発者がその準備ができているとは思いません。はい、Character の呼び出しがあり、答えは「int に貼り付けます」です。(各専門化は、JRE フットプリントに対して最大 100K に予測されます。)

トリック 2: プリミティブ ストリームを使用して、プリミティブ ドメインで行うのが最適な処理 (並べ替え、縮小) を公開していますが、ボックス化されたドメインで実行できるすべてのことを複製しようとはしていません。たとえば、Aleksey が指摘しているように、IntStream.into() はありません。(ある場合、次の質問は「IntCollection はどこですか? IntArrayList? IntConcurrentSkipListMap?)必要な変換の数を減らします (たとえば、int -> T のマップのオーバーロードがない、int -> T の Function の特殊化がないなど)。

おそらく、将来、Java で値型がサポートされるようになったときに、これらのインターフェイスを取り除くことができるようになるでしょう (または、少なくとも使用する必要がなくなるでしょう)。

専門家グループは、これだけでなく、いくつかの設計上の問題に取り組みました。下位互換性を維持する必要性、要件、または制約により、物事が困難になりました。その後、値型の欠如、型消去、およびチェック例外などの他の重要な条件があります。Java に最初の 2 つがあり、他の 2 つがなかったら、JDK 8 の設計は大きく異なっていたでしょう。したがって、多くのトレードオフを伴う難しい問題であり、EG はどこかで線を引いて決定を下さなければならなかったことを理解する必要があります。

于 2014-03-27T15:05:48.103 に答える
2

これは、すべての基本的な操作機能に、自動ボックスおよびボックス解除操作のコストが発生することを意味するためです。

これによるパフォーマンスへの影響を気にしない場合は、ボックス版の を使用してFunction<>ください。

于 2014-03-27T14:46:42.337 に答える