Javaの型消去の利点は何ですか?


98

今日私は言ったつぶやきを読みました:

Javaユーザーが型の消去について文句を言うのはおかしいです。これは、Javaが正しくなった唯一のものであり、間違ったものはすべて無視しています。

したがって、私の質問は:

Javaの型消去のメリットはありますか?下位互換性とランタイムパフォーマンスのためのJVM実装設定以外に(おそらく)提供する技術的またはプログラミングスタイルの利点は何ですか?


6
質問を「意見に基づく」として閉じたくない場合は、別の方法で質問してください。(「型消去の技術的な利点は何か」のように)。さらに、ツイーターに実際に何を言っているか尋ねてください。
スティーブンC

3
「なぜそれが良いのか」を削除します。「良いこと」は特許の主観的な特徴であり、タイプの消去良いことであるという疑問を明らかにします...
スティーブンC

3
このサイトがこれまでにTwitterに投稿されたすべてについての質問に退化する場合、神は私たちを助けてください。少なくとも、質問に対して信頼できるソースを引用してください。
ローンの侯爵2014年

21
質問が「信頼できるソース」からのものである必要があるのはなぜですか?それはばかげている。
vertti

4
さて、ここでのほとんどの問題は、特定のソフトウェア/言語/その他の問題に関する質問者自身の問題を除いて、すべてのソースからのものではありません。
vertti

回答:


90

型消去は良い

事実に固執しよう

これまでの回答の多くは、Twitterユーザーに過度に関係しています。メッセンジャーではなくメッセージに集中することは役に立ちます。これまでに述べた抜粋だけでもかなり一貫したメッセージがあります:

Javaユーザーが型の消去について文句を言うのはおかしいです。これは、Javaが正しくなった唯一のものであり、間違ったものはすべて無視しています。

私は大きな利益(例:パラメトリック性)とゼロのコスト(申し立てられたコストは想像力の限界です)を得ます。

new Tは壊れたプログラムです。「すべての命題は真実である」という主張に同型である。私はこれに大したことはありません。

目標:合理的なプログラム

これらのつぶやきは、マシンに何かをさせることができるかどうかではなく、マシンが実際に望むことを行うと推論できるかどうかという観点を反映しています。適切な推論は証拠です。証明は、正式な表記またはあまり正式でない表記で指定できます。仕様言語に関係なく、それらは明確で厳密でなければなりません。非公式な仕様を正しく構成することは不可能ではありませんが、実際のプログラミングにはしばしば欠陥があります。私たちは、非公式な推論で抱えている問題を補うために、自動化された探索的テストなどの修正を行います。これは、テストが本質的に悪い考えであると言っているわけではありませんが、引用されているTwitterユーザーは、もっと良い方法があることを示唆しています。

したがって、私たちの目標は、マシンが実際にプログラムを実行する方法に対応する方法で、明確かつ厳密に推論できる正しいプログラムを持つことです。ただし、これが唯一の目標ではありません。また、ロジックにはある程度の表現力を持たせたいと考えています。たとえば、命題論理で表現できるのは非常に限られています。一次論理のようなものから普遍的(and)と実存的(∃)の定量化があると便利です。

推論のための型システムの使用

これらの目標は、型システムによって非常に適切に対処できます。これは、カリーハワード対応のため特に明確です。この対応は多くの場合、次の類推で表されます。型はプログラムに対するものであり、定理は証明に対するものです。

この対応はやや深遠です。論理式を取り、型への対応を通じてそれらを変換できます。次に、コンパイルする同じ型シグネチャを持つプログラムがある場合、論理式が普遍的に真であることを証明しました(トートロジー)。これは、対応が双方向であるためです。タイプ/プログラムと定理/証明の世界の間の変換は機械的であり、多くの場合自動化できます。

Curry-Howardは、プログラムの仕様で何をしたいかをうまく果たしています。

型システムはJavaで役立ちますか?

Curry-Howardを理解していても、型システムの価値を無視するのは簡単な場合があります。

  1. 作業が非常に難しい
  2. (Curry-Howardを介して)表現力が制限されたロジックに対応
  3. 壊れています(「弱い」または「強い」としてシステムの特性を取得します)。

最初の点に関しては、おそらくIDEはJavaの型システムを操作するのに十分簡単に​​します(これは非常に主観的です)。

2番目の点に関しては、Javaはたまたま1次のロジックにほぼ対応しています。ジェネリックスは、普遍的な数量化と同等の型システムを使用します。残念ながら、ワイルドカードは実存的な定量化のほんの一部しか提供しません。しかし、普遍的な定量化はかなり良いスタートです。Aは完全に制約されていないので、関数List<A>すべての可能なリストに対して普遍的に機能すると言えます。これは、「パラメトリック性」に関してTwitterユーザーが話していることにつながります。

パラメトリック性についてよく引用される論文は、フィリップワドラーの定理です。。このホワイトペーパーの興味深い点は、型シグネチャのみから、非常に興味深い不変式を証明できることです。これらの不変条件の自動テストを作成する場合、時間を大幅に浪費することになります。たとえば、の場合List<A>、型シグネチャのみからflatten

<A> List<A> flatten(List<List<A>> nestedLists);

私たちはそれを推論することができます

flatten(nestedList.map(l -> l.map(any_function)))
     flatten(nestList).map(any_function)

これは単純な例であり、おそらく非公式にその理由を説明することができますが、型システムからそのような証明を正式に無料で入手し、コンパイラーによってチェックすると、さらに便利になります。

消去しないと虐待につながる可能性があります

言語実装の観点から見ると、Javaのジェネリック(ユニバーサルタイプに対応)は、プログラムの動作に関する証明を得るために使用されるパラメトリック性に大きく関与しています。これは、言及された3番目の問題になります。これらすべての証明と正確さの向上には、欠陥のない実装された健全なタイプのシステムが必要です。Javaには、私たちの推論を打ち砕くことができるいくつかの言語機能があります。これらには以下が含まれますが、これらに限定されません。

  • 外部システムとの副作用
  • 反射

消去されていないジェネリックは、多くの点でリフレクションに関連しています。消去せずに、アルゴリズムの設計に使用できる実装に含まれる実行時情報があります。これが意味することは、静的にプログラムについて推論すると、全体像がわからないということです。リフレクションは、静的に推論する証拠の正当性を厳しく脅かします。それは偶然の一致ではなく、さまざまなトリッキーな欠陥につながります。

では、消去されていないジェネリックが「役に立つ」と思われる方法は何でしょうか。ツイートで言及されている使用法を考えてみましょう:

<T> T broken { return new T(); }

Tに引数のないコンストラクタがない場合はどうなりますか?一部の言語では、取得するものがnullです。または、null値をスキップして、直接例外を発生させることもできます(null値はとにかくつながるようです)。私たちの言語はチューリング完全であるため、どの呼び出しがbroken引数なしのコンストラクターを持つ「安全な」型に関係し、どれが関係しないのかを推論することは不可能です。プログラムが普遍的に機能するという確信が失われました。

消去とは、私たちが推論したことを意味します(消去しましょう)

したがって、プログラムについて推論したい場合は、推論を強く脅かす言語機能を使用しないことを強くお勧めします。それをしたら、実行時に型を単にドロップしないのはなぜですか?それらは必要ありません。キャストが失敗しないか、呼び出し時にメソッドが欠落しているかもしれないという満足感で、効率とシンプルさを得ることができます。

消去すると推論が促進されます。


7
私がこの反応をまとめるために取った時間の間に、他の何人かの人々は同様の答えで答えました。でもこれだけ書いたので、捨てるのではなく投稿することにしました。
Sukant Hajra 14

32
これに関して、型の消去は、下位互換性を損なうことなく機能をJavaに持ち込むための、片思いの試みでした。それは強みではありません。パラメータのないコンストラクタなしでオブジェクトをインスタンス化しようとする可能性があることが主な懸念である場合、正しい答えは、「パラメータのないコンストラクタを持つ型」の制約を許可することであり、言語機能を損なわないようにすることです。
基本的な

5
基本的に、尊敬の念として、あなたはまとまりのある議論をしていません。プログラムについて推論するための説得力のあるツールとしてのコンパイル時の証明の価値を完全に無視して、消去が「不自由」であると主張しているだけです。さらに、これらのメリットは、Javaがジェネリックを実装するためにとった動機と動機と完全に直交しています。また、これらの利点は、消去の実装がはるかに賢明な言語にも当てはまります。
Sukant Hajra 2015

44
ここで提示する議論は消去に反対するものではなく、リフレクション(および一般に実行時の型チェック)に対するものです。基本的に、リフレクションは悪いと主張しているため、消去が消去とうまく機能しないという事実は良いことです。それはそれ自体で有効なポイントです(多くの人が反対するでしょうが)。しかし、Javaにはすでにリフレクション/ランタイムチェックがあり、そうではなく、なくなることはないため、コンテキストではあまり意味がありません。だから、言語の既存の、非廃止予定の一部が一緒にプレイしない構造を持っているある制限、あなたはそれを見てどんなに。
Pavel Minaev 2016年

10
あなたの答えは単に話題外です。抽象概念としての型消去が型システムの設計に一般的に役立つ理由について、長続きする(独自の用語では興味深い)説明を入力しますが、トピックは、「型消去」というラベルの付いたJava Genericsの特定の特性の利点です。 「これは表面的にはあなたが説明する概念に似ているだけであり、興味深い不変条件を提供することで完全に失敗し、不合理な制約を導入しています。
Marko Topolnik 2016年

40

タイプは、コンパイラーがプログラムの正当性をチェックできるようにプログラムを作成するために使用される構造です。タイプは値の命題です-コンパイラはこの命題が真であることを検証します。

プログラムの実行中、型情報は必要ありません。これはコンパイラーによって既に確認されています。コンパイラーは、コードで最適化を実行するためにこの情報を自由に破棄する必要があります。コードをより高速に実行し、より小さなバイナリーを生成します。型パラメーターの消去により、これが容易になります。

Javaは、実行時に型情報(リフレクション、instanceofなど)を照会できるようにすることで、静的型付けを中断します。これにより、静的に検証できないプログラムを構築できます-プログラムは型システムをバイパスします。また、静的な最適化の機会を逃します。

型パラメーターが消去されるという事実は、これらの誤ったプログラムの一部のインスタンスが構築されるのを防ぎますが、型情報がさらに消去され、リフレクションおよびinstanceof機能が削除された場合、より多くの誤ったプログラムは許可されません。

消去は、データ型の「パラメトリック性」の特性を維持するために重要です。コンポーネントタイプTを介してパラメーター化された「リスト」タイプがあるとします。つまり、List <T>です。その型は、このリスト型がすべての型Tで同じように機能するという命題です。Tが抽象の無制限の型パラメーターであるという事実は、この型について何も知らないため、Tの特別な場合に特別なことは何もできないことを意味します。

たとえば、リストxs = asList( "3")があるとします。要素を追加します:xs.add( "q")。私は["3"、 "q"]で終わります。これはパラメトリックなので、List xs = asList(7);と想定できます。xs.add(8)は[7,8]で終わる

さらに、List.add関数はTの値を薄い空気から生み出すことができないことも知っています。asList( "3")に "7"が追加されている場合、可能な回答は値 "3"と "7"からのみ作成されることを知っています。関数がリストを作成できないため、リストに「2」または「z」が追加される可能性はありません。これらの他の値のどちらも追加するのが賢明ではなく、パラメトリック性により、これらの誤ったプログラムの構築が防止されます。

基本的に、消去は、パラメトリック性に違反するいくつかの手段を防ぎ、静的型付けの目的である誤ったプログラムの可能性を排除します。


15

(私はすでにここで回答を書いていますが、2年後にこの質問を再訪して、別の完全に異なる回答方法があることに気付いたので、以前の回答をそのまま残して、これを追加します。)


Java Genericsで行われたプロセスが「型消去」という名前に値するかどうかは、非常に議論の余地があります。ジェネリック型は消去されずに、対応する生の型に置き換えられるため、「型の切断」がより適切な選択のようです。

一般的に理解されている意味での型消去の典型的な機能は、アクセスするデータの構造に対して「ブラインド」にすることで、ランタイムを静的型システムの境界内に留めることです。これはコンパイラに全力を与え、静的型のみに基づいて定理を証明することを可能にします。また、コードの自由度を制限してプログラマーを支援し、単純な推論により多くの力を与えます。

Javaの型消去はそれを達成しません—次の例のように、コンパイラーを不自由にします:

void doStuff(List<Integer> collection) { 
}

void doStuff(List<String> collection) // ERROR: a method cannot have 
                   // overloads which only differ in type parameters

(上記の2つの宣言は、消去後に同じメソッドシグネチャにまとめられます。)

反対に、ランタイムはオブジェクトのタイプとその理由を検査できますが、真のタイプへの洞察は消去によって損なわれるため、静的タイプ違反は簡単に達成でき、防止するのは困難です。

さらに複雑にするために、元の型シグネチャと消去された型シグネチャは共存し、コンパイル時に並行して考慮されます。これは、プロセス全体がランタイムから型情報を削除することではなく、下位互換性を維持するためにジェネリック型システムをレガシーraw型システムにシューホーンすることに関するためです。この宝石は典型的な例です:

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

(冗長extends Object性は、消去された署名の下位互換性を維持するために追加する必要がありました。)

それを念頭に置いて、引用をもう一度見てみましょう。

Javaユーザーが型の消去について文句を言うのはおかしい

Javaは正確に何を正しくしましたか?意味に関係なく、単語そのものですか?対照的に、控えめなint型を見てください。実行時の型チェックは実行されず、実行も不可能であり、実行は常に完全に型保証されています。これが、型消去が正しく行われたときの様子です。そこにあるかどうかさえわかりません。


10

ここでまったく考慮されていないことの1つは、OOPの実行時のポリモーフィズムは、実行時の型の具体化に基本的に依存していることです。修正された型によってバックボーンが保持されている言語が、その型システムに主要な拡張機能を導入し、型消去に基づいている場合、認知的不協和は必然的な結果です。これがまさにJavaコミュニティに起こったことです。型消去が多くの論争を呼んでいるのはそのためであり、最終的にはJavaの将来のリリースで型消去を元に戻す計画があるのです面白いものを見つけるJavaユーザーの不満では、Javaの精神に対する正直な誤解、または意識的に非難されている冗談を裏切っています。

「Javaが正しかったのは消去だけだ」という主張は、「ランタイム型の関数引数に対する動的ディスパッチに基づくすべての言語には根本的な欠陥がある」という主張を意味します。確かにそれ自体が正当な主張であり、Javaを含むすべてのOOP言語の正当な批評と見なすこともできますが、ランタイムのポリモーフィズムが行われるJavaのコンテキスト内で機能を評価および批判するための重要なポイントとしてそれ自体を提出することはできません。公理的です。

要約すると、「型消去は言語設計への道である」と正当に述べているかもしれませんが、Java内で型消去をサポートする立場は、それがあまりにも遅すぎて、歴史的な瞬間でさえすでにそうであったという理由誤って配置されていますオークがSunに採用され、Javaに改名されたとき。



静的型付け自体がプログラミング言語の設計における適切な方向性であるかどうかに関しては、これは、プログラミングの活動を構成すると私たちが考えるもののはるかに広い哲学的コンテキストに適合します。数学の古典的な伝統から明確に派生した1つの思考の学校は、プログラムを1つの数学的概念または他の例(命題、関数など)のインスタンスと見なしますが、プログラミングを機械に話しかけ、機械に何が欲しいかを説明します。この見方では、プログラムは動的で有機的に成長するエンティティであり、静的に型付けされたプログラムの注意深く構築された建物の劇的な反対です。

動的言語をその方向への一歩であると見なすのは自然なことです。プログラムの一貫性はボトムアップで現れ、トップダウンでそれを課すアプリオリな制約はありません。このパラダイムは、私たち人間が開発と学習を通じて私たちが存在するようになるプロセスをモデル化するためのステップと見なすことができます。


それをありがとう。この質問に対して私が期待するのは、関連する哲学に対するより深い理解の正確な種類です。
Daniel Langdon

動的ディスパッチは、具体化された型にまったく依存しません。多くの場合、プログラミング文献が「タグ」と呼んでいるものに依存していますが、それでもタグを使用する必要はありません。(オブジェクトの)インターフェースを作成し、そのインターフェースの匿名インスタンスを作成すると、具体化された型をまったく必要とせずに動的ディスパッチを実行することになります。
Sukant Hajra 2016

@sukanthajra髪の毛を割るだけです。タグは、インスタンスが示す動作の種類を解決するために必要なランタイム情報です。それは静的型システムの範囲外であり、それが議論されている概念の本質です。
Marko Topolnik 2016年

「推論型」と呼ばれるまさにこの推論のために設計された非常に静的な型が存在します。これは「バリアントタイプ」とも呼ばれます(ベンジャミンピアースの非常に優れたタイプとプログラミング言語の本では)。したがって、これは髪の毛の分割ではありません。また、完全にタグなしのアプローチでは、カタモフィズム/フォールド/ビジターを使用できます。
Sukant Hajra 2016

理論の授業をありがとうございましたが、関連性がわかりません。私たちのトピックは、Javaの型システムです。
Marko Topolnik 2016年

9

同じ会話の同じユーザーによる後続の投稿:

new Tは壊れたプログラムです。「すべての命題は真実である」という主張に同型である。私はこれに大したことはありません。

(これは、アイデアが「それはいくつかの状況の新しいT '良いだろうに思える」、すなわちこと、他のユーザーによる声明に応じたnew T()消去を入力することにより不可能である(これは議論の余地がある- 。場合でもTで使用可能でした実行時、それは抽象クラスまたはインターフェースである可能性がありますVoid。または、引数なしコンストラクタがないか、引数なしコンストラクタがプライベートである可能性があります(たとえば、シングルトンクラスであると想定されているため)、またはno-argコンストラクターは、総称メソッドがキャッチまたは指定しないチェック済み例外を指定できますが、それは前提です。消去せずに、少なくともT.class.newInstance()これらの問題を処理するを記述できることは事実です。))

型は命題に同型であるというこの見解は、ユーザーが形式的な型理論の背景を持っていることを示唆しています。(S)彼は「動的型」や「実行時型」を好まない可能性が高く、ダウンキャストinstanceofやリフレクションなどのないJavaを好みます。(Standard MLのような、非常に豊富な(静的)型システムを持ち、動的セマンティクスが型情報にまったく依存しない言語を考えてください。)

ところで、ユーザーがトローリングしていることは覚えておく価値があります。(静的に)タイプされた言語を心から好む可能性がありますが、そのビューの他の人を誠実に説得しようとはしていません。むしろ、元のツイートの主な目的は、反対する人をあざけることであり、反対する人の一部が口にした後、ユーザーは次のようなフォローアップのつぶやきを投稿しました。 Javaのユーザーとは異なり、彼らはやっています。」残念ながら、これは彼が実際に何を考えているかを見つけるのを難しくします。しかし幸いにも、そうすることはそれほど重要ではないことも意味します。自分の意見への実際の深さを持つ人々は、一般的に荒らしに頼らないでください、かなりこのコンテンツフリー。


2
型消去があるため、Javaでコンパイルされません
Mark Rotteveel 14年

1
@MarkRotteveel:ありがとうございます。それを説明する(そしてコメントする)ための段落を追加しました。
ruakh

1
賛成票と反対票の混合から判断すると、これは私がこれまでに投稿した中で最も物議を醸す回答です。不思議に誇りに思っています。
ruakh 2014

6

良い点の1つは、ジェネリックが導入されたときにJVMを変更する必要がなかったことです。Javaは、ジェネリックをコンパイラレベルでのみ実装します。


1
何と比較した場合のJVMの変更?テンプレートでは、JVMの変更も必要ありません。
ローン侯爵

5

タイプ消去が良いことである理由は、それが不可能にすることが有害であることです。実行時に型引数が検査されないようにすると、プログラムの理解と推論が容易になります。

直観に反していることがわかったのは、関数のシグネチャがより一般的であると、理解しやすくなるということです。これは、可能な実装の数が減るためです。このシグネチャを持つメソッドについて考えてみましょう。このメソッドには副作用がないことがわかっています。

public List<Integer> XXX(final List<Integer> l);

この関数の可能な実装は何ですか?非常に多くの。この関数が何をするかはほとんどわかりません。入力リストを逆にする可能性があります。intをペアにして合計し、サイズの半分のリストを返すこともできます。想像できる他の多くの可能性があります。今検討してください:

public <T> List<T> XXX(final List<T> l);

この関数の実装はいくつありますか?実装は要素のタイプを認識できないため、多数の実装を除外できます。要素を組み合わせたり、リストに追加したり、フィルターで除外したりすることはできません。アイデンティティ(リストへの変更なし)、要素の削除、またはリストの反転などの制限があります。この関数は、その署名だけに基づいて推論するのが簡単です。

Java以外では、常に型システムをだますことができます。このジェネリックメソッドの実装では、instanceofチェックや任意の型へのキャストなどを使用できるため、型シグネチャに基づく推論は簡単に役に立たなくなる可能性があります。この関数要素のタイプを検査し、その結果に基づいてさまざまなことを実行できます。これらのランタイムハックが許可されている場合、パラメーター化されたメソッドシグネチャは私たちにとってはるかに役に立たなくなります。

Javaが型の消去を行わなかった場合(つまり、型引数が実行時に具体化された場合)、これにより、この種の推論を損なう悪意がさらに増える可能性があります。上記の例では、リストに少なくとも1つの要素がある場合、実装は型シグネチャによって設定された期待に違反することができます。しかしT具体化されていれば、リストが空であっても具体化できます。具象化された型は、コードの理解を妨げる可能性を(すでに非常に多く)増やすだけです。

型消去は、言語の「強力さ」を低下させます。しかし、「力」のいくつかの形態は実際には有害です。


1
ジェネリックスはJava開発者が「理解して推論」するには複雑すぎるか、またはチャンスが与えられたとしても足元を撃たないことが信頼できないと主張しているようです。後者はJavaの思想と同期しているように見えますが(unsigned型などはありません)、開発者には少し不満があるようです
Basic

1
@ベーシック私は自分の表現を非常に貧弱にしていたに違いありません。問題はジェネリックが複雑であることではなく、キャストのようなものinstanceofが型に基づいてコードが何をするかを推論する能力を妨げることです。Javaが型引数を具体化すると、この問題はさらに悪化します。実行時に型を消去すると、型システムがより便利になります。
Lachlan、2015年

@lachlanは私にとっては完全に理にかなっており、通常の読み取りではJava開発者への侮辱を感じることはないと思います。
ロブ・グラント

3

これは直接的な回答ではありません(OPは「メリットは何ですか」と尋ねました。「デメリットは何ですか」と返信しています)

C#の型システムと比較すると、Javaの型消去は2つの理由で非常に困難です。

インターフェースを2回実装することはできません

C#では、特に2つのタイプが共通の祖先を共有しない場合(つまり、それらの祖先がである場合)、両方IEnumerable<T1>IEnumerable<T2>安全に実装できます。 Object

実際の例:Spring Frameworkでは、ApplicationListener<? extends ApplicationEvent>複数回実装することはできません。Tテストに基づいて異なる動作が必要な場合instanceof

新しいT()を実行することはできません

(それを行うにはクラスへの参照が必要です)

他の人がコメントしnew T()たようにClass<T>、のインスタンスを呼び出して、コンストラクターに必要なパラメーターを確認することによってのみ、同等のものを行うことはリフレクションを介してのみ行うことができます。C#では、パラメーターなしのコンストラクターに制約する場合にnew T() のみ実行できますTTその制約を尊重しない場合、コンパイルエラーが発生します。

Javaでは、多くの場合、次のようなメソッドを作成する必要があります。

public <T> T create(....params, Class<T> classOfT)
{

    ... whatever you do
    ... you will end up
    T = classOfT.newInstance();


    ... or more advanced reflection
    Constructor<T> parameterizedConstructorThatYouKnowAbout = classOfT.getConstructor(...,...);
}

上記のコードの欠点は次のとおりです。

  • Class.newInstanceは、パラメータのないコンストラクタでのみ機能します。利用できない場合ReflectiveOperationExceptionは、実行時にスローされます
  • 反映されたコンストラクタは、上記のようなコンパイル時の問題を強調しません。リファクタリングした場合、引数を交換した場合は、実行時にのみわかります

私がC#の作成者であれば、コンパイル時に検証しやすい1つ以上のコンストラクター制約を指定する機能を導入しました(たとえば、パラメーターを持つコンストラクターが必要になる場合がありますstring,string)。でも最後は憶測です


2

その他のポイントは、他のどの回答も考慮していないようです:ランタイム型指定でジェネリックが本当に必要な場合は、次のように自分で実装できます

public class GenericClass<T>
{
     private Class<T> targetClass;
     public GenericClass(Class<T> targetClass)
     {
          this.targetClass = targetClass;
     }

このクラスは、Javaが消去を使用しなかった場合にデフォルトで達成できるすべてのことを実行できます。新しいTs(T使用する予定のパターンに一致するコンストラクターがあると想定)、またはTsの配列を割り当てることができます。特定のオブジェクトがであるかどうかを実行時に動的にテストしT、それに応じて動作を変更します。

例えば:

     public T newT () { 
         try {
             return targetClass.newInstance(); 
         } catch(/* I forget which exceptions can be thrown here */) { ... }
     }

     private T value;
     /** @throws ClassCastException if object is not a T */
     public void setValueFromObject (Object object) {
         value = targetClass.cast(object);
     }
}

誰もが反対票の理由を説明したいと思いますか?これは、私が知る限り、Javaの型消去システムの想定されるマイナス面が実際には実際の制限ではない理由の完全に良い説明です。だから問題は何ですか?
ジュール

私は賛成しています。実行時の型チェックをサポートしているわけではありませんが、このトリックはソルトマイニングで便利です。
techtangents 2014

3
Java ソルトマインズの言語であり、実行時の型情報がないため、これは必要不可欠なものです。うまくいけば、型消去は将来のバージョンのJavaで元に戻され、ジェネリックスがより便利な機能になるでしょう。現在、コンセンサスは彼らの推力/重量比が1を下回っていることである
マルコTopolnik

まあ、確かに... TargetTypeがジェネリック自体を使用しない限り。しかし、それはかなり制限のようです。
基本的な

ジェネリック型でも動作します。唯一の制約は、インスタンスを作成する場合、適切な引数型のコンストラクターが必要ですが、私が知っている他のすべての言語には同じ制約があるため、制限がありません。
Jules 14

0

同じコードが複数の型に使用されるため、C ++のようなコードの膨張を回避します。ただし、型の消去には仮想ディスパッチが必要ですが、c ++-code-bloatアプローチでは仮想的にディスパッチされないジェネリックを実行できます


3
ただし、これらの仮想ディスパッチのほとんどは実行時に静的ディスパッチに変換されるため、見た目ほど悪くはありません。
PiotrKołaczkowski14年

1
非常に真実です。実行時にコンパイラーを使用できるため、JavaはC ++に比べて多くの優れた機能(および高いパフォーマンス)を実現できます。
ネクロマンサー2014年

うわー、私はどこかのそれを書き留めてみましょう。.. JavaがCPPに高いパフォーマンスの相対を実現
jungle_mole

1
@jungle_moleはい、ありがとうございます。実行時にコンパイラーを使用してプロファイリング後に再度コードをコンパイルできることの利点を理解している人はほとんどいません。(またはガベージコレクションがbig-oh(non-garbage)ではなく、ガベージコレクションがbig-oh(garbage)であるという素朴で誤った信念ではない(またはガベージコレクションはわずかな労力であるが、ゼロコストを許可することで補正する)割り当て))。人々が自分のコミュニティが信じていることを盲目的に採用し、第一原理からの推論をやめる悲しい世界です。
ネクロマンサー2017年

0

ほとんどの回答は、実際の技術的な詳細よりもプログラミングの哲学に関心があります。

この質問は5年以上前のものですが、疑問は残ります。なぜ、技術的な観点から型消去が望ましいのでしょうか。結局のところ、答えは(より高いレベルで)かなり単純です:https : //en.wikipedia.org/wiki/Type_erasure

C ++テンプレートは実行時に存在しません。コンパイラーは呼び出しごとに完全に最適化されたバージョンを発行します。つまり、実行は型情報に依存しません。しかし、JITは同じ関数の異なるバージョンをどのように扱いますか?一つの機能を持たせた方がいいのでは?JITがそのすべての異なるバージョンを最適化する必要があるとは思わないでしょう。では、タイプセーフについてはどうでしょうか。それは窓の外に出なければならないことを推測します。

しかし、少し待ってください:.NETはどのように実行しますか?反射!この方法では、1つの関数を最適化するだけで、実行時の型情報を取得できます。そして、それが.NETジェネリックが以前より遅くなった理由です(ただし、はるかに改善されています)。それが便利ではないと私は主張していません!しかし、それは高価であり、絶対に必要でない場合は使用しないでください(コンパイラ/インタープリタはとにかくリフレクションに依存しているため、動的型付き言語では高価とは見なされません)。

このように、型消去を使用した一般的なプログラミングはオーバーヘッドがゼロに近くなります(一部のランタイムチェック/キャストが引き続き必要です):https : //docs.oracle.com/javase/tutorial/java/generics/erasure.html

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.