Java 10のvar
inに次のような文字列を割り当てることができます。
var foo = "boo";
次のようなラムダ式を使用して割り当てることはできませんが、
var predicateVar = Apple -> Apple.getColor().equals("red");
String
、ArrayList
、ユーザークラスなどの残りを推論できるのに、なぜラムダまたはメソッド参照型を推論できないのですか?
ローカル変数型推論JEP から:
推論プロセスは、実質的に、変数にその初期化式の型を与えるだけです。いくつかの微妙な点:
- 初期化子にはターゲットタイプがありません(まだ推論していないため)。 lambdas、メソッド参照、配列初期化子など、そのような型を必要とするポリ式はエラーをトリガーします。
ラムダ式自体には型がないため、var
に対して推論することはできません。
...同様に、デフォルトのルールを設定できます。
もちろん、この制限を回避する方法を考え出すことができます。開発者がそれを行わないという決定を下した理由は、意思決定の一部である誰かがここで答えることができない限り、本当に推測にかかっています。とにかく興味がある場合は、openjdkメーリングリストのいずれかで質問できます。 http://mail.openjdk.Java.net/mailman/listinfo
私が推測するなら、彼らはおそらくvar
のコンテキストでラムダ推論を特定の機能インターフェース型のセットに結び付けたくなかったでしょう。より良い解決策は、ジェネリック関数型(つまり、(Apple) -> boolean
)互換性のある機能インターフェイスタイプに変換できます。しかし、JVMにはそのような関数型はなく、それらを実装しないという決定は、ラムダ式を作成したプロジェクトの間にすでに行われました。繰り返しになりますが、具体的な理由に興味がある場合は、開発者に問い合わせてください。
これはvar
とは関係ありません。ラムダにstandalone typeがあるかどうかに関係します。 var
が機能する方法は、RHSで初期化子のスタンドアロン型を計算し、それを推測することです。
Java 8、ラムダ式およびメソッド参照に導入されているため、スタンドアロン型はありません-機能するインターフェイスである必要があるターゲットタイプが必要です。
あなたがしようとした場合:
Object o = (String s) -> s.length();
また、コンパイラは、ラムダをどの関数インターフェイスに変換するのかわからないため、型エラーも発生します。
var
で推論を求めるのは難しくなりますが、簡単な質問には答えられないため、難しい質問にも答えられません。
他の方法(キャストなど)でターゲットタイプを指定でき、それが機能することに注意してください。
var x = (Predicate<String>) s -> s.isEmpty();
rHSにはスタンドアロン型があるためです。ただし、x
にマニフェストタイプを指定することで、ターゲットタイプを提供する方が適切です。
これは不可能、望ましくない、または望ましくないと言っているすべての人に、引数の型のみを指定することでScalaがラムダの型を推測できることを指摘したいだけです。
val predicateVar = (Apple: Apple) => Apple.getColor().equals("red")
また、Haskellでは、getColor
はオブジェクトにアタッチされないスタンドアロン関数であり、Hindley-Milnerの完全な推論を行うため、引数の型を指定する必要もありません。
predicateVar = \Apple -> getColor Apple == "red"
これは、プログラマーが明示的に指定するのが面倒な単純な型ではなく、より複雑な型であるため、非常に便利です。
言い換えると、Java 10の機能ではありません。これは、実装と以前の設計選択の制限です。
すでに数人が言及しているように、var
はどの型を推論すべきであり、なぜそうすべきなのでしょうか?
声明:
var predicateVar = Apple -> Apple.getColor().equals("red");
あいまいであり、コンパイラがFunction<Apple, Boolean>
over Predicate<Apple>
を選択する正当な理由はなく、ラムダのApple
識別子がApple
の実体を表すと仮定すると、その逆もありません。
もう1つの理由は、ラムダ自体にspeakableタイプがないため、コンパイラが推論する方法がないことです。
また、"これが可能であった場合"コンパイラがすべての機能インターフェイスを通過し、var
変数にラムダを割り当てるたびにどの機能インターフェイスが最も適切であるかを判断する必要があるため、オーバーヘッドを想像してください。
これに答えるには、詳細を調べ、ラムダとは何か、どのように機能するかを理解する必要があります。
まず、ラムダとは何かを理解する必要があります。
ラムダ式は常に機能的なインターフェイスを実装するため、インターフェイスを実装するまったく新しいクラスを作成する代わりに、Runnable
などの機能的なインターフェイスを提供する必要がある場合、ラムダ構文を使用して作成できます。機能インターフェースが必要とするメソッド。ただし、ラムダには、実装している機能インターフェイスの型がまだあることに注意してください。
それを念頭に置いて、これをさらに一歩進めましょう。
Runnableの場合と同様に、これは非常に機能します。機能インターフェイスを実装するためのまったく新しいオブジェクトを作成する代わりに、このnew Thread(()->{//put code to run here});
のような新しいスレッドを作成できます。これは、コンパイラがThread()
がRunnable型のオブジェクトを受け取ることを認識しているため、ラムダ式がどの型である必要があるかを認識しているために機能します。
ただし、ラムダをローカル変数に割り当てる場合、コンパイラはこのラムダが実装している機能インターフェイスをまったく知らないため、var
型を推測することはできません。ユーザーが作成した機能的なインターフェイスを実装している場合もあれば、runnable
インターフェイスである場合もあるため、知る方法はありません。
これが、ラムダがvarキーワードで機能しない理由です。
それは機能ではないからです:
この処理は、初期化子、拡張forループのインデックス、および従来のforループで宣言されたローカル変数を持つローカル変数に制限されます。メソッド形式、コンストラクタ形式、メソッド戻り型、フィールド、キャッチ形式、またはその他の種類の変数宣言には使用できません。