2017-10-08 13 views
1

私が正しく理解していれば、JVMの下で、ラムダ式を使うたびにオブジェクトを作成しなければなりません。ScalaのFunctionN型がAnyValではなくAnyRefのサブタイプとして作成されるのはなぜですか?

なぜオーバーヘッドですか? Functional型を設計するとき、AnyValの代わりにAnyRefを拡張することをScalaの作成者が選択したのはなぜですか?つまり、関数自体が、実際の '値'を持っていないので、関数が基本的なUnit表現(または等価チェックなどのためにハッシュを含むlong)を持つ値オブジェクトになることはできませんか?ラムダごとにオブジェクトを割り当てると、コードベースのパフォーマンスが向上するとは思いません。

AnyValを拡張することが私の頭に浮かぶ1つの明らかな欠点は、関数型のサブクラス化を禁止することです。おそらくそれだけでAnyValを拡張しないのに十分でしょうが、それ以外の理由はありますか?

--edit

ので削除(私は関数が他の変数の上にクローズする必要があることを理解し、私は適用方法、ないFunctionNオブジェクトのフィールドメンバへの引数として、それをモデル化するために、より自然なことだと思いますこの部分にjava.lang.Objectを持つ必要があります)。結局のところ、コンパイル時に知られているすべての変数が閉じているわけではありません。

- もう一度編集

私はそれについて知りました。私が気にしていたのは「ラムダ持ち上げ」でした。メソッドを呼び出す

+3

関数には「真の値」があります。 –

答えて

6

唯一の方法は、(ただし、インターフェイス上で、同一の)バイトコード操作invokevirtual(クラス上の仮想ディスパッチ)、invokeinterfaceあるinvokespecial(仮想参照。privateに使用さを無視して、正確所与のメソッドを呼び出し、 super,new)、およびinvokestatic召喚ユニコーンコールstatic方法)。 invokespecialは出力されています。なぜなら、まさにある関数を呼び出すことは、関数を抽象化することとは対照的です。 invokestaticも、引数を必要としない本質的にinvokespecialクローンであるため、出力されています。 とinvokeinterfaceは、私たちの目的が同じとみなされるのに十分似ています。

Cで見られるようなプレーン関数ポインタを渡す方法はありません。コード内の任意のポイントにジャンプできる命令がないため、できるだけそれを呼び出すことはできません。 (メソッド内のすべてのジャンプターゲットはそのメソッドに制限されており、外部への参照はすべて文字列になっています(ファイルがロードされるとJVMはその表現を自由に最適化することができます))

Inいずれかの命令でメソッドを呼び出すには、JVMはターゲットオブジェクトの仮想ディスパッチテーブル内のメソッドをルックアップする必要があります。 ()AnyValのサブクラスは2.10まで存在しませんでしたが、私たちの不信を停止させましょう)でオブジェクトをダミーアウトしようとすると、JVMは、近くにあるもの(おそらく興味深い)メソッドを呼び出そうとしたときにひどく混乱しますあなたが得ることができるように "フィーチャーレスBLOB"に。

また、オブジェクトのメソッドはそのクラスによって完全に決定されることに注意してください。 a.getClass == b.getClassの場合は、abは、と全く同じメソッド、コード、すべてを持っています。これを回避するには、FunctionN形質のサブクラスを作成し、各サブクラスが1つの関数を表し、各クラスの各インスタンスに、その関数に関連付けられたコードへの参照を含むクラスへの参照が含まれるようにする必要があります。invokedynamicであっても、LambdaMetaFactoryの現在の実装は実行時に内部クラスを作成するので、これはまだ真です。

最後に、@Olegが指摘するように、関数は状態を必要としないという仮定はfalseです。クロージャは、その環境への参照を保持する必要があります。これはオブジェクトでのみ可能です。

関連する問題