この記事では、 C#とJavaという2 つのプログラミング言語を比較します。この記事では主に言語とその機能に焦点を当てていますが、このような比較では必然的にプラットフォームとライブラリのいくつかの機能も考慮する必要があります。
C# と Java は、静的、強力、かつ明示的に型付けされた類似の言語です。どちらもオブジェクト指向で、半解釈または実行時ジャストインタイムコンパイルで設計されており、CやC++のような中括弧言語です。
種類
統一型システム
どちらの言語も、クラスベースのオブジェクト指向で静的に型付けされています。Java では、プリミティブ型はオブジェクト指向ではなく、言語自体を使用して定義できなかったという点で特殊です。また、参照型と共通の祖先を共有していません。Java の参照型はすべて、共通のルート型から派生しています。C#には、すべての型 (安全でないポインター[17]を除く) が最終的に共通のルート型から派生する統一型システムがあります。したがって、すべての型はこのルート型のメソッドを実装し、オブジェクト型に定義された拡張メソッドは、プリミティブintリテラルやデリゲートを含むすべての型に適用されます。これにより、C# は Java とは異なり、参照型ではないカプセル化されたオブジェクトをサポートできます。
Java では、複合型は参照型と同義です。つまり、クラス参照型でない限り、型にメソッドを定義することはできません。C# では、カプセル化とメソッドの概念は参照要件から切り離されているため、型は参照型でなくてもメソッドとカプセル化をサポートできます。ただし、仮想メソッドと特殊化をサポートするのは参照型だけです。
どちらの言語も、参照ではなく値によってコピーおよび渡される多くの組み込み型をサポートしています。Java ではこれらの型をプリミティブ型と呼び、C# では単純型と呼びます。プリミティブ型/単純型は通常、基盤となるプロセッサ アーキテクチャからネイティブにサポートされます。
C# の単純型は複数のインターフェイスを実装し、その結果、型のインスタンス、さらにはリテラルに対しても、多くのメソッドを直接提供します。C# の型名は、共通言語ランタイム(CLR) 型の別名でもあります。C# の型はlong型とまったく同じ型です。唯一の違いは、前者が正規の.NET名であるのに対し、後者はその C# 別名であることです。
System.Int64
Java は、プリミティブ型に直接メソッドを提供しません。代わりに、プリミティブ値を操作するメソッドは、コンパニオンプリミティブ ラッパー クラスを通じて提供されます。このようなラッパー クラスの固定セットが存在し、それぞれが固定セットのプリミティブ型のいずれかをラップします。たとえば、Java Long型は、プリミティブlong型をラップする参照型です。ただし、これらは同じ型で はありません。
データ型
数値型
符号付き整数
Java と C# はどちらも、8、16、32、64 ビットのビット幅を持つ符号付き整数をサポートしています。Javaではbyte 、C# ではsbyte (符号付きバイト) と呼ばれる 8 ビット整数を除き、型には同じ名前/エイリアスを使用します。
符号なし整数
C# は、符号付き整数型に加えて符号なし整数型もサポートします。符号なし型は、それぞれ 8、16、32、64 ビット幅のbyte、ushort、uint、ulongです。これらの型に対する符号なし算術演算もサポートされています。たとえば、2 つの符号なし整数 ( uint ) を加算すると、結果はuintになります。long や符号付き整数にはなりません。
Javaには符号なし整数型がありません。特に、Javaには符号なしバイトのプリミティブ型がありません。代わりに、Javaのバイト型は符号拡張されており、これがバグや混乱の原因となることがよくあります。[18]
符号なし整数は Java から意図的に除外されました。これは、James Gosling が、プログラマーは符号なし演算の仕組みを理解できないだろうと考えたためです。
プログラミング言語の設計において、よくある問題の一つは、言語が複雑になりすぎて誰も理解できなくなることです。私が試した小さな実験の一つは、C言語の符号なし演算のルールについて人々に尋ねることでした。結局、C言語の符号なし演算がどのように機能するか誰も理解していないことがわかりました。人々が理解している明らかなことがいくつかありますが、多くの人は理解していません。[9] [19]
Javaバージョン8と9では、いくつかの限定された組み込みの符号なし整数演算が追加されましたが、それらはプリミティブラッパークラスの静的メソッドとしてのみ公開されています。これらは符号付きプリミティブ整数型を操作し、符号なしであるかのように扱います。[20]
高精度の小数点数
C#には、金融計算や通貨計算に適した高精度(28桁)の10進演算用の型とリテラル表記があります。[21] [22] [23] floatやdoubleデータ型とは異なり、0.1などの10進小数は10進表現で正確に表現できます。floatやdouble表現では、このような数値は2進数展開が終了しないことが多く、丸め誤差が発生しやすくなります。[22]
Java にはこのような組み込み型はありませんが、Java ライブラリには任意精度の10 進数型があります。これは言語型とは見なされず、通常の算術演算子をサポートしません。むしろ、型メソッドを使用して操作する必要がある参照型です。任意サイズ/精度の数値の詳細については、以下を参照してください。
高度な数値型
どちらの言語も、任意サイズの整数と小数点の計算用に、 ライブラリ定義の任意精度の算術型を提供します。
任意精度の小数点計算用のデータ型があるのは Java だけです。複素数を扱うための型があるのは C# だけです。
どちらの言語でも、高度な数値型で実行できる演算の数は、組み込みのIEEE 754浮動小数点型に比べて制限されています。たとえば、任意サイズの型では平方根や対数はサポートされていません。
C#では、カスタムの暗黙的/明示的な変換と演算子のオーバーロードを使用して、ライブラリ定義型を既存の型や演算子と統合できます。ライブラリ定義型の統合のセクションの例を参照してください。
キャラクター
どちらの言語も、単純な型としてネイティブのchar (文字) データ型を備えています。char型はビット演算子で使用できますが、これは演算の前に char 値を整数値に昇格することによって実行されます。したがって、どちらの言語でも、ビット演算の結果は文字ではなく数値型になります。
組み込みの複合データ型
どちらの言語も、文字列を参照型の(不変の) オブジェクトとして扱います。どちらの言語でも、型には文字列の操作、解析、フォーマットなどを行うためのいくつかのメソッドが含まれています。どちらの言語でも、正規表現は外部機能と見なされ、別のクラスで実装されています。
両言語のライブラリは、異なるカルチャの日付、時刻、タイムゾーン、およびカレンダーを操作するためのクラスを定義します。Java はjava.util.Date、ミリ秒精度の可変参照型である と、(Java 8 以降) ナノ秒精度の不変参照型のセットjava.timeである パッケージ (日付のみ、時刻のみ、日付と時刻の値用の LocalDate 、 LocalTime 、 LocalDateTime などのクラスを含む)を提供します。 [ 24]対照的に、C# は、100ナノ秒精度の日付と時刻情報用の不変の struct 値型です。.NET 6 API では、日付のみまたは時刻のみの操作用に同様の構造体であるとも追加されました。 [25] C# では、さらに期間を操作するための型が定義されています。Java 8では、同じ目的で クラスが提供されています。両言語とも、異なるカルチャとタイムゾーンに応じた日付と時刻の演算をサポートしています。
System.DateTimeSystem.DateOnlySystem.TimeOnlySystem.TimeSpanjava.time.Duration
ユーザー定義の値型 (構造体)
C# では、プログラマーはstructキーワードを使用して、ユーザー定義の値型を作成できます。クラスや標準プリミティブとは異なり、このような値型は参照ではなく値によって渡され、割り当てられます。また、オブジェクトの一部 (フィールドまたはボックス化) にすることも、クラス型に通常存在するメモリ間接参照なしで配列に格納することもできます。
値型にはnull値の概念がなく、初期化せずに配列で使用できるため、常に暗黙のデフォルト コンストラクターが付属しており、基本的に構造体のメモリ領域がゼロで埋められます。プログラマーは、1 つ以上の引数を持つ追加のコンストラクターのみを定義できます。値型には仮想メソッド テーブルがないため、(および固定メモリ フットプリントのため) 暗黙的にシールされます。ただし、値型はインターフェイスを実装できます(実際に頻繁に実装されます) 。たとえば、組み込みの整数型は、いくつかのインターフェイスを実装します。
組み込みのプリミティブ型以外に、Java には値型の概念は含まれていません。
列挙
どちらの言語も列挙型を定義していますが、その実装方法は根本的に異なります。そのため、列挙型は、2 つの言語間でコードを自動的に変換するように設計されたツール (Java から C# へのコンバーターなど) が失敗する領域の 1 つです。
C#はCと同様の方法で列挙を実装しており、プリミティブ整数型(int、byte、shortなど)に実装されたビットフラグのラッパーとして実装しています。これによりパフォーマンス上の利点があり、C/C++でコンパイルされたコードとの相互作用が向上しますが、提供される機能が少なく、C#言語で許可されているように低レベルの値型が列挙型に直接キャストされるとバグが発生する可能性があります。したがって、これは糖衣構文と見なされています。[26]対照的に、Javaは列挙をインスタンスのフル機能のコレクションとして実装しており、より多くのメモリを必要とし、C/C++コードとの相互作用には役立ちませんが、リフレクションと固有の動作で追加機能を提供します。各言語での実装については、次の表で説明します。
C#とJavaの両方で、プログラマーは文字列やプリミティブ整数型に変換せずにswitch文で列挙型を使用できます。ただし、C#では、case文にコードが含まれていない限り暗黙的なフォールスルーは許可されません。これは、見つけにくいバグの一般的な原因であるためです。[29]フォールスルーは、 goto文を使用して明示的に宣言する必要があります。[30]
デリゲート、メソッド参照
C# は、オブジェクト指向メソッド ポインターをデリゲートの形式で実装します。デリゲートは、メソッドへの参照を取得できる特殊な型です。この参照は、デリゲート型の変数に格納するか、デリゲート パラメーターを介してメソッドに渡して後で呼び出すことができます。C# デリゲートは共変性と反変性をサポートし、シグネチャ互換の静的メソッド、インスタンス メソッド、匿名メソッド、またはラムダ式への参照を保持できます。
デリゲートをクロージャやインライン関数と混同しないでください。クロージャ/インライン関数への参照は、デリゲート参照でキャプチャされて初めて有用となるため、これらの概念は関連しています。ただし、デリゲートは常にインライン関数を参照するわけではなく、既存の静的メソッドまたはインスタンス メソッドを参照することもできます。デリゲートは C# イベントの基礎を形成しますが、これらとも混同しないでください。
デリゲートは、Java では不必要で言語に有害であると考えられ、またパフォーマンスの問題が発生する可能性があるため、意図的に除外されました。[31]代わりに、代替メカニズムが使用されます。ラッパー パターンは、クライアントが既知のインターフェイスを介して 1 つ以上のクライアント定義メソッドにアクセスできるようにするという点で C# のデリゲートに似ており、そのようなメカニズムの 1 つです。[要出典]もう 1 つは、内部クラスを使用するアダプターオブジェクトの使用です。Java の設計者は、これはバインドされたメソッド参照よりも優れたソリューションであると主張しました。[31]
C# デリゲートの例と同等の Java 構成要素も参照してください。
リフトされた(null 許容)型
C# では、値/プリミティブ/単純型を「リフト」して、型のネイティブ値に加えて特殊なnull値を許可できます。型は、型名にサフィックスを追加することでリフトされます。これは、ジェネリック型を使用することと同じです。ここで、Tはリフトされる型です。変換は、ベースとリフトされた型の間の値を変換するために暗黙的に定義されます。リフトされた型は、nullと比較したり、 HasValueをテストしたりできます。また、リフトされた演算子は、リフトされていないベースに基づいて暗黙的かつ自動的に定義されます。この場合、一部のブール演算子を除き、null 引数が結果に伝播されます。
?Nullable<T>
Java は型のリフトという概念をサポートしていませんが、すべての組み込みプリミティブ型には対応するラッパー型があり、参照型 (クラス) であるため null値をサポートしています。
Java 仕様によれば、null参照を逆参照しようとすると、実行時に例外、具体的には NullPointerException がスローされることになります。 (定義上、メモリ内のオブジェクトを指していないため、それ以外の場合は逆参照しても意味がありません。) これは、nullと評価されるラッパー型の変数をアンボックス化しようとする場合にも当てはまります。アンボックス化するオブジェクトがないため、プログラムは例外をスローします。したがって、後続の計算に参加するボックス化された値はありません。
次の例は、異なる動作を示しています。C# では、lifted*operator はオペランドの null値を伝播しますが、Java では、null 参照をアンボックス化すると例外がスローされます。
C# のすべてのリフト演算子は、オペランドの 1 つがnullの場合に、無条件にnull を伝播するように定義されているわけではありません。具体的には、ブール演算子は3 項ロジックをサポートするようにリフトされているため、 SQLとの互換性が保たれます。
Java ブール演算子は三項論理をサポートしておらず、基本クラス ライブラリにも実装されていません。
遅延バインディング(動的)型
C# は、リフレクションなしの動的呼び出し、動的言語との相互運用性、および (たとえば) ドキュメント オブジェクト モデルへのアドホック バインディングをサポートする遅延バインド動的型を備えています。動的型は、コンパイル時に静的/仮想的に解決するのではなく、実行時に動的にメンバー アクセスを解決します。メンバー検索メカニズムは、従来のリフレクションをフォールバック メカニズムとして 使用して拡張可能です。
C# の動的型の使用例はいくつかあります。
- リフレクションのより簡潔な使用: インスタンスを動的型にキャストすることで、リフレクション API を直接使用せずに、プロパティ、メソッド、イベントなどのメンバーをインスタンス上で直接呼び出すことができます。
- 動的言語との相互運用性: 動的型には、動的に型付けされたオブジェクトを実装するためのハブアンドスポークサポートと、効率的なメンバー検索のための共通ランタイム インフラストラクチャが付属しています。
- 動的抽象化をオンザフライで作成する: たとえば、動的オブジェクトを使用すると、XMLやXHTMLドキュメントなどのドキュメント オブジェクト モデルへのより簡単なアクセスが可能になります。
Java は遅延バインディング型をサポートしていません。C# 動的型の使用例には、Java での対応する構成要素が異なります。
- 既存の型の動的な遅延バインディングによる名前による呼び出しには、リフレクションを使用する必要があります。
- 動的言語との相互運用性を確保するには、その言語に固有の何らかの相互運用性 API を使用する必要があります。Java仮想マシンプラットフォームには複数の動的言語が実装されていますが、言語間でオブジェクトを渡す方法についての共通標準はありません。通常、これには何らかの形式のリフレクションまたはリフレクションのような API が関係します。Java から JavaFX オブジェクトを使用する方法の例として、[32] があります。
- ドキュメント オブジェクト モデル抽象化との対話など、実行時に完全にオブジェクトを作成して対話するには、特定の抽象化 API を使用する必要があります。
動的言語との相互運用性の例も参照してください。
ポインタ
Java は、Java ランタイム環境内でのポインターとポインター演算を排除します。Java 言語設計者は、ポインターはプログラマーがコードにバグを組み込む主な機能の 1 つであると判断し、ポインターをサポートしないことを選択しました。[9] Java では、オブジェクトや構造体を基盤となるオペレーティング システムと直接やり取りすることはできないため、オブジェクトや構造体を特定のメモリ レイアウト (ポインターが頻繁に使用されるレイアウト) に合わせてモデル化する必要はありません。Java と基盤となるオペレーティング システムとの通信は、代わりにJava Native Interface (JNI) に基づいており、基盤となるオペレーティング システムとの通信や適応は外部の接着層を介して処理されます。
C# ではポインターとそれに対応するポインター演算の使用が許可されていますが、C# 言語の設計者も、ポインターがオブジェクト アクセスの厳格な規則を回避するために使用される可能性があるという同様の懸念を抱いていました。そのため、C# ではデフォルトでポインターも使用できません。[33]ただし、多くのネイティブ関数の呼び出しにはポインターが必要なので、明示的なunsafeモードではポインターが許可されます。ポインターを使用するコード ブロックまたはメソッドは、ポインターを使用できるようにunsafeキーワードでマークする必要があり、コンパイラーではそのようなコードのコンパイルを許可するスイッチが必要です。スイッチを使用してコンパイルされたアセンブリは、そのようにマークされ、明示的に信頼されている場合にのみ実行できます。これにより、ポインターとポインター演算を使用して、ネイティブ メモリ レイアウトを使用してオペレーティング システムまたはその他のネイティブ API と直接オブジェクトを渡したり受け取ったりできると同時に、そのような潜在的に危険なコードを特に信頼されたアセンブリに分離することもできます。
/unsafe/unsafe
参照タイプ
どちらの言語でも、参照は中心的な概念です。クラスのすべてのインスタンスは参照によって行われます。
言語構文 自体には直接現れていませんが、どちらの言語も弱参照の概念をサポートしています。弱参照によってのみ参照されるインスタンスは、まったく参照がない場合と同様にガベージ コレクションの対象となります。どちらの言語でも、この機能は実際にはコア ランタイム機能ですが、関連するライブラリを通じて公開されています。
弱参照の他に、Java にはソフト参照があります。これらは弱参照とよく似ていますが、Java 仮想マシン(JVM) は、メモリが必要になるまでソフト参照されたオブジェクトの割り当てを解除しません。
配列とコレクション
配列の宣言とアクセスに使用される構文は同じですが、C# では多次元配列を宣言および操作するための構文が追加されています。
多次元配列では、局所性が向上するためパフォーマンスが向上する場合があります(ジャグ配列の場合のように配列の各次元に 1 つではなく 1 つのポインター参照があるため)。ただし、多次元配列のすべての配列要素アクセスには 2 つ以上の次元間の乗算/シフトが必要であるため、これは非常にランダムなアクセスのシナリオでのみ利点となります。
もう 1 つの違いは、多次元配列全体を 1 回の演算子newの適用で割り当てることができるのに対し、ジャグ配列では各次元ごとにループと割り当てが必要になることです。ただし、Java では、ジャグ配列を通常の長さで割り当てるための構文構造が提供されており、ループと複数の割り当ては仮想マシンによって実行されるため、ソース レベルで明示的に行う必要はありません。
どちらの言語も、さまざまな順序付きおよび順序なしのリスト、マップ/辞書、セットなどを含む広範なコレクション タイプを備えています。
タプル
C# では、タプル型 (積型とも呼ばれる) を作成するための 2 つの異なる方法が用意されています。1 つ目はクラスを使用する方法ですSystem.Tuple。クラスは、汎用タプル型を作成するためのフレームワーク API (.NET Framework 4.0 以降) によって提供される不変の参照型です。[14]この最初の方法は、System.ValueTupleフレームワーク API (.NET Framework 4.7 以降) によって提供される可変の値型である 2 番目の構造体によって事実上置き換えられました。[15]
2 つのメソッドは表面的には似ているように見えますが、注目すべき違いがいくつかあります。ValueTuple型は値型なので、メモリ フットプリントがよりコンパクトです。また、ValueTuple型はTupleクラスの不変プロパティと比較して、その内容を可変フィールドとして公開します。最後に、C# バージョン 7.0 以降、言語にはタプルをValueTupleインスタンスとして構築、分解、および操作するためのネイティブ構文サポートがあります。これにより、タプルの構成フィールドの名前を任意に変更することもできます ( Tupleでは、フィールドの名前は常にItem1、Item2などになります)。
Javaはタプル型を言語や標準APIの一部として提供していません。タプル型を提供できるサードパーティのライブラリは数多く存在しますが[41]、それらはすべて必然的にC#クラスに似ています。C#のValueTupleSystem.Tuple型とその関連構文と比較すると、それらは扱いにくいです(作成するにはコンストラクタまたは静的ファクトリメソッドを明示的に使用する必要があり、分解するには個々のメンバーアクセスが必要であり、要素の名前が固定されています)。
式と演算子
箱詰めと開封
どちらの言語も自動的なボックス化とアンボックス化が可能です。つまり、任意のプリミティブ型と対応する参照型の間で暗黙的なキャストが可能になります。
C# では、プリミティブ型は Object 型のサブタイプです。Java ではこれは当てはまりません。プリミティブ型とそれに対応するラッパー型は、オートボックス化とアンボックス化を除いて、特定の関係を持ちません。オートボックス化とアンボックス化は、両者の交換のための構文糖として機能します。これは、自動キャストが許可されず、プログラマーがプリミティブ型とラッパー (参照) 型階層の 2 つの別々の型セットで作業していた以前のバージョンの Java との下位互換性を維持するために意図的に行われました。[46]
この違いによって、次のような結果が生じます。まず、C# では、プリミティブ型で、オブジェクトのメソッドのオーバーライドなどのメソッドを定義できます。Java では、このタスクはプリミティブ ラッパー クラスToString()によって実行されます。
第二に、Java ではプリミティブ値を直接逆参照しようとすると、自動的にボックス化されないため、追加のキャストが必要になります。式は、Java では整数リテラルを文字列に変換しますが、 C# では同じ操作を実行します。これは、後者がプリミティブ値42のインスタンス呼び出しであるのに対し、前者は 型のオブジェクトに対するインスタンス呼び出しであるためです。
((Integer)42).toString()42.ToString()java.lang.Integer
最後に、もう 1 つの違いは、Java ではジェネリックでボックス化された型を多用することです(以下を参照)。
声明
構文
どちらの言語も、C/C++ ファミリーの「中括弧」言語と見なされています。全体的に、言語の構文は非常に似ています。ステートメントおよび式レベルの構文は、C/C++ の伝統から明らかに影響を受けており、ほぼ同一です。型定義レベル (クラスおよびインターフェイス) では、若干の違いがあります。Java はクラスの拡張とインターフェイスの実装について明示的に規定していますが、C# は、新しいクラス/インターフェイスが派生する型の種類からこれを推測します。
C# は Java よりも多くの機能をサポートしており、これは Java よりも多くのキーワードと文法規則を指定する構文にもある程度表れています。
キーワードと下位互換性
言語が進化するにつれ、両言語の言語設計者は、新しいキーワードや構文で言語を拡張したいという状況に直面してきました。特に新しいキーワードは、ソース レベルで既存のコードを壊す可能性があります。つまり、古いコードを新しいバージョンの言語のコンパイラに渡すと、コンパイルできなくなる可能性があります。言語設計者は、このような退行を回避しようとしています。この問題に対処する際、2 つの言語の設計者は異なる道をたどってきました。
Java 言語の設計者は、新しいキーワードをできる限り避け、代わりに以前は有効でなかった新しい構文構造を導入するか、既存のキーワードを新しいコンテキストで再利用することを好みました。このようにして、下位互換性を危険にさらすことはありませんでした。前者の例は、forループが反復可能な型を受け入れるように拡張された方法に見られます。後者の例は、 Java 1.5 でジェネリックが導入されたときに、 extendsキーワードと (特に) superキーワードが型境界の指定に再利用された方法に見られます。かつて (Java 1.4)、以前はキーワードとして予約されていなかった新しいキーワードassertが導入されました。たとえば、コードがassert を識別子として使用していた場合、これにより以前は有効だったコードが無効になる可能性があります。設計者は、この問題を次の 4 段階のソリューションで解決することを選択しました。1) Java 1.4 以降を使用する必要があるかどうかを示すコンパイラ スイッチを導入する、2) Java 1.4 以降としてコンパイルする場合にのみassert をキーワードとしてマークする、3) 以前の (1.4 に対応していないコード) が無効にならないように 1.3 をデフォルトにする、4) キーワードが Java 1.3 モードで使用される場合は警告を出してコードの変更を許可する。
C# 言語設計者は、最初のバージョン以降、いくつかの新しいキーワードを導入してきました。ただし、これらのキーワードをグローバルキーワードとして定義するのではなく、コンテキスト依存キーワードとして定義しています。つまり、C# 2.0 でpartialキーワードやyieldキーワードなどが導入されたときでも、コンテキストを考慮すると、キーワードとしての使用と識別子としての使用が衝突する可能性はないため、これらの単語を識別子として使用することは依然として有効です。したがって、現在の C# 構文は、使用する言語バージョンを指定しなくても、以前のバージョン用に記述されたソース コードと完全に下位互換性があります。
オブジェクト指向プログラミング
C# と Java はどちらも、動的ディスパッチを使用するオブジェクト指向言語として根本から設計されており、構文はC++に似ています(C++ はCから派生しています)。ただし、どちらの言語も C または C++ のスーパーセットではありません。
部分クラス
C# では、部分クラスと呼ばれる機能を使用して、クラス定義を複数のソース ファイルに分割できます。各パーツは、キーワードpartialでマークする必要があります。すべてのパーツは、単一のコンパイルの一部としてコンパイラに提示する必要があります。パーツは、他のパーツのメンバーを参照できます。パーツはインターフェイスを実装でき、1 つのパーツは基本クラスを定義できます。この機能は、コード生成シナリオ (ユーザー インターフェイス(UI) 設計など) で役立ちます。コード生成シナリオでは、コード ジェネレーターが 1 つのパーツを提供し、開発者が別のパーツを提供して、一緒にコンパイルできます。これにより、開発者は、コード ジェネレーターが後でそのコードを上書きするリスクなしに、自分のパーツを編集できます。クラス拡張メカニズムとは異なり、部分クラスでは、コンパイル時に解決されることが保証されているため、パーツ間の 循環依存関係が許可されます。Java には、対応する概念はありません。
内部クラスとローカルクラス
どちらの言語でも、クラスが別のクラス内で語彙的に定義される 内部クラスが許可されます。ただし、各言語でこれらの内部クラスのセマンティクスはかなり異なります。
Javaでは、内部クラスがstaticと宣言されていない限り、内部クラスのインスタンスへの参照は外部クラスへの参照も伴う。その結果、内部クラスのコードは外部クラスのstaticメンバーとnon-staticメンバーの両方にアクセスできる。non-static内部クラスのインスタンスを作成するには、それを含む外部クラスのインスタンスに名前を付ける必要がある。[53]これはJDK 1.3で導入された新しいnew演算子によって行われる。これは外部クラスのインスタンスへの参照を持つどのクラスでも実行できる。
outerClassInstance.new Outer.InnerClass()
C# では、内部クラスは概念的に通常のクラスと同じです。ある意味では、外部クラスは名前空間としてのみ機能します。したがって、内部クラスのコードは、外部クラスのインスタンスへの明示的な参照を介してアクセスしない限り、外部クラスの非静的メンバーにアクセスできません。プログラマーは、内部クラスをプライベートとして宣言して、外部クラスのみがアクセスできるようにすることができます。
Java には、メソッド本体内で定義できるローカル クラスまたは匿名クラスと呼ばれる別の機能があります。これらは通常、1 つまたは 2 つのメソッド (通常はイベント ハンドラー) のみを持つインターフェイスを実装するために使用されます。ただし、スーパークラスの仮想メソッドをオーバーライドするためにも使用できます。これらのローカル クラスのメソッドは、 finalと宣言された外部メソッドのローカル変数にアクセスできます。C# は、匿名デリゲートを提供することでこれらのユースケースを満たしています。詳細については、イベント処理を参照してください。
C# には匿名型/クラスと呼ばれる機能もありますが、これは Java の同名の概念とはかなり異なります。この機能により、プログラマーは、クラスが持つべきプロパティの名前のセットと、それぞれを初期化する式のみを提供することで、クラスをインスタンス化できます。プロパティの型は、それらの式の型から推論されます。これらの暗黙的に宣言されたクラスは、オブジェクトから直接派生します。
イベント
C# マルチキャスト デリゲートは、イベントで使用されます。イベントは、イベント駆動型プログラミングのサポートを提供し、オブザーバー パターンの実装です。これをサポートするために、クラスでイベントを定義する特定の構文と、イベント ハンドラーを登録、登録解除、または結合する演算子があります。
Java でイベントが実装される方法については、ここを参照してください。
演算子のオーバーロードと変換
演算子のオーバーロードとユーザー定義のキャストは別々の機能であり、どちらも新しい型を型システムの第一級オブジェクトにすることを目的としている。C# でこれらの機能を使用することで、Complexや10 進数などの型が統合され、加算や乗算などの通常の演算子が新しい型で機能するようになった。C++ とは異なり、C# では演算子のオーバーロードの使用が制限されており、演算子new、、、、、および などの複合ステートメントのバリエーションではオーバーロードが禁止されている。ただし、複合演算子は、や の呼び出しのように、オーバーロードされた単純な演算子を呼び出します。[54]( )||&&=+=-=-=
Javaには演算子のオーバーロードやカスタム変換が含まれておらず、機能の乱用を防ぎ、言語をシンプルに保つために使用されています。[55]
インデクサー
C# には、演算子オーバーロードの特殊なケース (C++ など) またはパラメーター化されたget / setプロパティと見なすことができるインデクサーも含まれています。インデクサーは、1 つ以上のパラメーター (インデックス) を使用するという名前のプロパティです。インデックスは任意の型のオブジェクトにすることができます。
operator[]this[]
myList [ 4 ] = 5 ;文字列name = xmlNode . Attributes [ "name" ]; orders = customerMap [ theCustomer ];
Java にはインデクサーが含まれていません。一般的な Java パターンでは、C# プログラマーがインデクサーを使用する場所に明示的なゲッターとセッターを記述します。
フィールドと初期化
オブジェクトの初期化
C# と Java の両方で、オブジェクトのフィールドは、変数初期化子(定義されている変数に割り当てることができる式) またはコンストラクター(オブジェクトの作成時に実行される特別なサブルーチン) によって初期化できます。さらに、Java にはインスタンス初期化子が含まれます。これは、スーパークラスのコンストラクターへの明示的 (または暗黙的) な呼び出しの後、コンストラクターが実行される前に実行される、引数のない匿名のコード ブロックです。
C# は、オブジェクトを作成するときに、次の順序でオブジェクト フィールドを初期化します。
- 派生静的フィールド
- 派生した静的コンストラクタ
- 派生インスタンスフィールド
- 基本静的フィールド
- 基本静的コンストラクタ
- 基本インスタンスフィールド
- 基本インスタンスコンストラクター
- 派生インスタンスコンストラクタ
上記のフィールドの一部は適用できない場合があります (例: オブジェクトに静的フィールドがない場合)。派生フィールドはオブジェクトの直接のクラスで定義されているフィールドで、基本フィールドはオブジェクトのスーパークラスの 1 つで定義されているフィールドを指す用語です。スーパークラスの一部のフィールドがプライベートとして定義されている場合でも、メモリ内のオブジェクト表現には、そのクラスまたはそのスーパークラスのいずれかで定義されているすべてのフィールドが含まれることに注意してください。
フィールド初期化子は、コンストラクターが呼び出される前に必ず有効になります。これは、オブジェクトのクラスとそのスーパークラスのインスタンス コンストラクターは、フィールド初期化子が呼び出された後に呼び出されるためです。ただし、仮想メソッドが基本コンストラクターから呼び出される場合、オブジェクトの初期化に潜在的な罠があります。サブクラスでオーバーライドされたメソッドは、サブクラスで定義されているフィールドを参照する場合がありますが、フィールド初期化を含むサブクラスのコンストラクターは、基本クラスのコンストラクターの後に呼び出されるため、このフィールドは初期化されていない可能性があります。
Java では、初期化の順序は次のとおりです。
- 別のコンストラクタの呼び出し(オブジェクトのクラスまたはオブジェクトのスーパークラスのいずれか)
- インスタンス変数初期化子とインスタンス初期化子(ソースコードに現れる順序)
- コンストラクタ本体
C# と同様に、特定のコンストラクターを呼び出すことによって新しいオブジェクトが作成されます。コンストラクター内では、最初のステートメントが別のコンストラクターの呼び出しになる場合があります。これが省略されると、スーパークラスの引数なしのコンストラクターの呼び出しがコンパイラによって暗黙的に追加されます。それ以外の場合は、オブジェクトのクラスの別のオーバーロードされたコンストラクターを明示的に呼び出すか、スーパークラスのコンストラクターを呼び出すことができます。前者の場合、呼び出されたコンストラクターは再び別のコンストラクター (オブジェクトのクラスまたはそのサブクラスのいずれか) を呼び出し、チェーンは遅かれ早かれスーパークラスのコンストラクターの 1 つへの呼び出しで終わります。
別のコンストラクタが呼び出された後 (これによりスーパークラスのコンストラクタが直接呼び出され、Object クラスまで続きます)、オブジェクトのクラスで定義されたインスタンス変数が初期化されます。一部の変数に対して変数初期化子が明示的に定義されていない場合でも、これらの変数はデフォルト値に初期化されます。スーパークラスで定義されたインスタンス変数は、スーパークラスのコンストラクタが呼び出されたときにそのコンストラクタによって初期化されているため、この時点ですでに初期化されていることに注意してください (コンストラクタのコードによって、またはコンストラクタのコードの前に実行された変数初期化子によって、あるいは暗黙的にデフォルト値に初期化されます)。Java では、変数初期化子はソース ファイル内のテキスト順序に従って実行されます。
最後に、コンストラクタ本体が実行されます。これにより、適切な初期化順序が保証されます。つまり、オブジェクト クラスのフィールドの初期化が始まる前に、基本クラスのフィールドの初期化が完了します。
Java のオブジェクト初期化には、主に 2 つの潜在的な落とし穴があります。まず、変数初期化子はメソッド呼び出しを含むことができる式です。メソッドはクラスで定義された任意の変数を参照できるため、変数初期化子で呼び出されるメソッドは、初期化される変数の下に定義されている変数を参照できます。初期化順序は変数定義のテキスト順序に対応するため、このような変数は初期化子で規定された値に初期化されず、デフォルト値が含まれます。もう 1 つの潜在的な落とし穴は、派生クラスでオーバーライドされたメソッドが基本クラス コンストラクターで呼び出される場合です。これは、派生クラスのオブジェクトが作成されるときにプログラマが予期しない動作を引き起こす可能性があります。初期化順序に従って、基本クラス コンストラクターの本体は、変数初期化子が評価される前、および派生クラス コンストラクターの本体が実行される前に実行されます。ただし、基本クラス コンストラクターから呼び出されるオーバーライドされたメソッドは、派生クラスで定義された変数を参照できますが、これらの変数は、初期化子で指定された値または派生クラス コンストラクターで設定された値にまだ初期化されていません。後者の問題は C# にも当てはまりますが、C# ではメソッドはデフォルトでオーバーライドできないため、それほど重大ではありません。
資源の処分
どちらの言語も、メモリの明示的な割り当て解除ではなく、メモリ リソースを再利用するための手段として、主にガベージ コレクションを使用します。どちらの場合も、オブジェクトがファイル ハンドル、グラフィック リソースなど、メモリ以外の異なる種類のリソースを保持している場合は、アプリケーションがそれを使用しなくなったときに明示的に通知する必要があります。C# と Java はどちらも、このような確定的な破棄のためのインターフェイスを提供し、C# と Java (Java 7 以降) はどちらも、それらのインターフェイスで破棄/クローズ メソッドを自動的に呼び出す自動リソース管理ステートメントを備えています。
方法
拡張メソッドとデフォルトメソッド
C# では、メソッドの最初のパラメータに特別なthis指定子を使用すると、メソッドは最初のパラメータの型のメンバー メソッドであるかのように動作できます。この外部クラスの拡張は、純粋に構文上のものです。拡張メソッドは、静的として宣言し、純粋に静的なクラス内で定義する必要があります。メソッドは、クラス外部の他のメソッドと同様に、メンバー アクセス制限に従う必要があります。したがって、静的メソッドはオブジェクトのカプセル化を破ることはできません。[61] [62]「拡張」は、静的ホスト クラスの名前空間がインポートされているスコープ内でのみアクティブになります。
Java 8 以降、Java にはデフォルト メソッドと呼ばれる同様の機能があります。これは、インターフェースで宣言された本体を持つメソッドです。C# 拡張メソッドとは対照的に、Java のデフォルト メソッドは、それらを宣言するインターフェースのインスタンス メソッドです。インターフェースを実装するクラスでのデフォルト メソッドの定義はオプションです。クラスでメソッドが定義されていない場合は、代わりにデフォルトの定義が使用されます。
C# 拡張メソッドと Java デフォルト メソッドの両方で、クラスはそれぞれ拡張/デフォルト メソッドのデフォルト実装をオーバーライドできます。どちらの言語でも、このオーバーライドは、メソッドの代替実装を使用するクラスでメソッドを定義することによって実現されます。
C# のスコープ ルールでは、一致するメソッドがクラスに見つかった場合、一致する拡張メソッドよりも優先されることが定義されています。Java では、クラス自体がメソッドを実装しない限り、デフォルト メソッドを持つインターフェイスを実装するように宣言されたクラスは、デフォルト メソッドの実装を持つものとみなされます。
部分メソッド
部分クラスに関連して、C# では部分クラス内で部分メソッドを指定できます。部分メソッドは、シグネチャにいくつかの制限があるメソッドの意図的な宣言です。この制限により、定義がどのクラス部分でも提供されていない場合、メソッドとそのすべての呼び出しを安全に削除できます。[63]この機能により、これらの拡張ポイントがコンパイル時に別のクラス部分で使用されていない場合、コードは実行時のオーバーヘッドを支払うことなく、多数のインターセプション ポイント (テンプレート メソッドの GoFデザイン パターンなど) を提供できます。Java には対応する概念はありません。
仮想メソッド
C# のメソッドは、デフォルトでは非仮想であり、必要に応じて明示的に仮想として宣言する必要があります。Java では、非静的で非プライベートなメソッドはすべて仮想です。仮想性により、メソッドの最新のオーバーライドが常に呼び出されることが保証されますが、これらの呼び出しは通常はインライン化できず、仮想メソッド テーブルを介した間接呼び出しが必要になるため、呼び出し時に一定の実行時コストが発生します。ただし、Oracle リファレンス実装を含む一部の JVM 実装では、最も一般的に呼び出される仮想メソッドのインライン化が実装されています。
Java メソッドは、デフォルトでは仮想です (ただし、 final修飾子を使用してオーバーライドを禁止することでシールできます)。派生クラスで同じ名前の新しい無関係なメソッドを定義する ことはできません。
つまり、Java ではデフォルトで、C# では明示的に有効にした場合のみ、派生クラスで新しいメソッドをその基本クラスと同じ名前とシグネチャで定義できます。このようなオブジェクトのスーパークラス参照でメソッドが呼び出されると、参照されているオブジェクトの特定のサブクラスに従って、基本クラスのメソッドの「最も深い」オーバーライドされた実装が呼び出されます。
場合によっては、サブクラスが、基本クラスに既に存在するメソッドと同じ名前とシグネチャを持つメソッドを導入すると、問題が発生する可能性があります。Java では、これは、どちらのクラスの設計者の意図でなくても、派生クラスのメソッドが基本クラスのメソッドを暗黙的にオーバーライドすることを意味します。
これを緩和するために、C# では、メソッドが継承されたメソッドをオーバーライドすることを意図している場合、overrideキーワードを指定する必要があります。そうしないと、メソッドは継承されたメソッドを「隠す」ことになります。キーワードがない場合、この影響に関するコンパイラ警告が発行されますが、newキーワードを指定することでこれを抑制できます。これにより、派生クラスで既に使用されているシグネチャを持つ非プライベート メソッド (つまり、名前空間の継承された部分) で基本クラスが拡張されることで発生する可能性のある問題を回避できます。Java には、メソッド アノテーションの形式で同様のコンパイラ チェックがありますが、これは必須ではなく、これがない場合、ほとんどのコンパイラはコメントを提供しません (ただし、メソッドはオーバーライドされます)。
@Override
定数/不変パラメータ
Java では、キーワードを使用してローカル変数またはメソッドパラメータの再割り当てを防ぐことができますfinal。このキーワードをプリミティブ型の変数に適用すると、変数は不変になります。ただし、final参照型の変数に適用すると、別のオブジェクトが割り当てられることを防ぐだけです。オブジェクトに含まれるデータが変更されることを防ぐことはできません。C#7 では、inキーワードを使用してメソッドパラメータの再割り当てを防ぐことができますが、このキーワードはローカル変数には使用できません。Java と同様に、パラメータにin を適用しても、パラメータが異なる値に再割り当てされることを防ぐだけです。オブジェクトに含まれるデータを変更することは可能です。[64]
どちらの言語も、メソッドを定数にする C / C++に存在するconst の正確さという重要な機能をサポートしていません。
Java では、「定数」という単語が任意にフィールドとして定義されます。慣例的に、これらの変数名は大文字のみで、単語はアンダースコアで区切られますが、Java 言語ではこれは必須ではありません。 のみのパラメータは定数とは見なされませんが、 のようなプリミティブ データ型や不変クラスの場合は定数と見なされることがあります。
static finalfinalString
ジェネレータメソッド
IEnumerable、IEnumerator、またはこれらのインターフェイスのジェネリック バージョンを返すように宣言された C# メソッドは、yield構文を使用して実装できます。これは、制限されたコンパイラ生成の継続の形式であり、シーケンスをトラバースまたは生成するために必要なコードを大幅に削減できますが、そのコードはコンパイラによって生成されるだけです。この機能は、フィボナッチ数列などの無限シーケンスを実装するためにも使用できます。
Java には同等の機能はありません。代わりに、ジェネレーターは通常、よく知られたコレクションまたは反復可能なインターフェースの特殊な実装を提供することで定義され、要求に応じて各要素を計算します。このようなジェネレーターをfor eachステートメントで使用するには、インターフェースを実装する必要がありますjava.lang.Iterable。
下記のフィボナッチ数列の例も参照してください。
明示的なインターフェース実装
C# には明示的なインターフェイス実装もあり、クラスが独自のクラス メソッドとは別にインターフェイスのメソッドを具体的に実装したり、2 つの基本インターフェイスから継承された同じ名前とシグネチャを持つ 2 つのメソッドに異なる実装を提供したりすることができます。
どちらの言語でも、メソッド (C# ではプロパティ) が複数のインターフェイスで同じ名前とシグネチャで指定されている場合、それらのインターフェイスを実装するクラスを設計すると、メンバーが衝突します。実装では、デフォルトですべてのインターフェイスに共通のメソッドを実装します。個別の実装が必要な場合 (メソッドが別の目的を果たすため、またはインターフェイス間で戻り値が異なるため)、C# の明示的なインターフェイス実装によって問題は解決されますが、オブジェクトの現在のキャストに応じて、同じメソッドに対して異なる結果が発生する可能性があります。Java では、名前の衝突を避けるために 1 つ以上のインターフェイスをリファクタリングする以外に、この問題を解決する方法はありません。[58]
参照(入力/出力)パラメータ
Java では、メソッドへのプリミティブ型 (例: int、double) の引数は値で渡されますが、オブジェクトは参照で渡されます。つまり、メソッドは実際の変数ではなく、渡されたプリミティブのコピーに対して動作します。逆に、実際のオブジェクトは変更できる場合もあります。次の例では、オブジェクト String は変更されません。クラス 'a' のオブジェクトが変更されます。
C#では、C++やある意味ではCと同様に、 refキーワードを使用して参照を強制することができます。C #のこの機能は、複数のオブジェクトを返すメソッドを作成する場合に特に便利です。Javaでは、ラッパー(この場合は「Ref」)を使用しない限り、メソッドから複数の値を返すことはサポートされていません。[65]
例外
チェック例外
Java は、チェック例外(および非チェック例外) をサポートします。C# は非チェック例外のみをサポートします。チェック例外では、プログラマーはメソッドでスローされた例外を宣言するか、try-catch句を使用してスローされた例外をキャッチする必要があります。
チェック例外は、すべてのエラーが確実に処理されるようにすることで、優れたプログラミング手法を奨励することができます。しかし、 C#言語のチーフアーキテクトであるアンダース・ヘルスバーグは、チェック例外はある程度Javaでの実験であり、小さなサンプルプログラム以外では価値があるとは示されていないと主張しています。[67] [68]
批判の 1 つは、チェック例外により、プログラマーが空の catch ブロック ( ) を使用するようになり、[69]例外がより高いレベルの例外処理ルーチンに伝播されるのではなく、例外を黙って飲み込むようになるというものです。ただし、例外をラッパー例外で再スローすることで、代わりに例外の連鎖を適用できる場合もあります。たとえば、オブジェクトがファイルではなくデータベースにアクセスするように変更された場合、呼び出し元はオブジェクトの内部動作を知る必要がない可能性があるため、をキャッチして として再スローすることができます。
catch (Exception e) {}SQLExceptionIOException
しかし、すべてのプログラマーがこのスタンスに賛成しているわけではない。ジェームズ・ゴスリングらは、チェック例外は有用であり、それを誤用すると問題が発生すると主張している。確かに、例外を黙ってキャッチすることは可能だが、その例外をどう処理するかを明示的に記述する必要がある。一方、チェックされていない例外はデフォルトで何もしない。例外を無視することはできるが、それを無視するように明示的にコードを書く必要がある。[70] [71]
try-catch-finally を使う
2 つの言語には、ステートメントの扱い方にも違いがありますtry-finally。tryブロックにthrowやreturnなどの制御を渡すステートメントが含まれている場合でも、finallyブロックは常に実行されます。 Java では、tryブロックが何らかの値を持つreturnステートメントによって終了し、その後に実行されるfinallyブロックも異なる値を持つreturnステートメントによって終了すると、予期しない動作が発生する可能性があります。 C# では、 finallyブロックでreturnやbreakなどの制御を渡すステートメントを禁止することでこの問題を解決しています。
ブロックを使用する一般的な理由はtry-finally、リソース管理コードを保護し、finally ブロックで貴重なリソースが確実に解放されるようにするためです。C# では、この一般的なシナリオの構文上の省略形としてusingステートメントが採用されており、usingのオブジェクトのメソッドが常に呼び出されます。
Dispose()
かなり微妙な違いは、例外がスローされたときにスタック トレースが作成される瞬間です。Java では、スタック トレースは例外が作成された瞬間に作成されます。
クラス Foo { Exception up = new Exception (); int foo () throws Exception { throw up ; } }
上記のステートメントの例外には、foo が何回呼び出されても、常にコンストラクターのスタック トレースが含まれます。一方、C# では、スタック トレースは "throw" が実行された瞬間に作成されます。
クラスFoo { Exception e = new Exception ();
int foo () { try { throw e ; } catch (例外e ) { throw ; } } }
上記のコードでは、例外には最初の throw 行のスタック トレースが含まれます。例外をキャッチするときに、例外を再スローする必要がある場合には 2 つのオプションがあります。throwは元の例外を元のスタックで再スローしますが、while は新しいスタック トレースを作成します。
throw e
最後にブロック
Java では、入力方法に関係なく、制御フローがtryステートメントのfinallyブロックから出ることができます。これにより、別の制御フロー ステートメント ( returnなど) が実行の途中で終了する可能性があります。例:
int foo () { try { return 0 ; } finally { return 1 ; } }
上記のコードでは、tryブロック内のreturnステートメントによって制御がブロックから外れ、実際に return が行われる前にfinallyブロックが実行されます。ただし、 finallyブロック自体も return を実行します。したがって、ブロックに入る原因となった元の return は実行されず、上記のメソッドは 0 ではなく 1 を返します。簡単に言うと、0 を返そうとしますが、最終的には1 を返します。
C# では、 throwを除き、制御フローがfinallyブロックを途中で抜けることを許可するステートメントは許可されません。特に、return はまったく許可されず、ターゲット ラベルがfinallyブロックの外側にある場合はgotoは許可されず、最も近い囲みループがfinallyブロックの外側にある場合はcontinueとbreakは許可されません。
ジェネリック
ジェネリックの分野では、2 つの言語は表面的には構文的に類似していますが、根底には深い違いがあります。
型消去と具体化されたジェネリック
Java のジェネリックは言語のみの構造であり、コンパイラでのみ実装されます。生成されたクラスファイルには、ジェネリック シグネチャがメタデータの形式でのみ含まれます (これにより、コンパイラは新しいクラスをそのシグネチャに対してコンパイルできます)。ランタイムはジェネリック型システムを認識しません。ジェネリックはJava 仮想マシン(JVM) の一部ではありません。代わりに、ジェネリック クラスとメソッドは、コンパイル時に型消去と呼ばれるプロセスによって変換されます。この間、コンパイラはすべてのジェネリック型を生のバージョンに置き換え、その型とそのメソッドが使用されているクライアント コードに適切なキャスト/チェックを挿入します。結果のバイト コードには、ジェネリック型またはパラメータへの参照は含まれません ( 「Java のジェネリック」も参照)。
Java言語仕様では、ジェネリックの特定の使用を意図的に禁止しています。これは、型消去によるジェネリックの実装と移行互換性を可能にするために必要です。[72] Javaプラットフォームに具体化されたジェネリックを追加する研究は、プロジェクトValhallaの一環として進行中です。
C# は、仮想実行システムのジェネリックのサポートを基盤としています。つまり、これは単なる言語機能ではありません。この言語は、CLRのクロスランゲージ ジェネリック サポートのフロントエンドにすぎません。コンパイル時にジェネリックの正しさが検証されますが、ジェネリックを実装するためのコード生成は、クラスのロード時に延期されます。クライアント コード (ジェネリック メソッド/プロパティを呼び出すコード) は完全にコンパイルされ、ジェネリックがタイプ セーフであると想定しても問題ありません。これを具体化と呼びます。実行時に、ジェネリック クラス/メソッド/デリゲートの一意の型パラメーター セットが初めて検出されたときに、クラス ローダー/検証ツールが具体的なクラス記述子を合成し、メソッド実装を生成します。メソッド実装の生成中は、すべての参照型が 1 つの型と見なされます。参照型は同じ実装を安全に共有できるためです。これは、単にコードを実装するためだけのものです。異なる参照型セットには、一意の型記述子が残ります。それらのメソッド テーブルは、単に同じコードを指すだけです。
以下のリストは、ジェネリックを管理する際のJavaとC#の違いを示しています。ただし、網羅的なものではありません。[73]
C# では、ジェネリックを直接プリミティブ型に使用できます。Java では、代わりにボックス化された型を型パラメータとして使用できます (の代わりに など)。これにはコストがかかります。このような値はすべて、使用時にボックス化/アンボックス化される必要があり、すべてヒープ割り当てされる必要があるためです。ただし、ジェネリック型は、Java のプリミティブ型の配列型で特殊化できます (例 )。[78]
いくつかのサードパーティライブラリは、プリミティブ型が提供するランタイムとメモリの最適化を維持するために、プリミティブ配列をサポートする Java の基本コレクションを実装しました。[79]List<Integer>List<int>List<int[]>
移行互換性
Javaの型消去設計は、移行互換性を実現する設計要件によって動機付けられました。これは、下位互換性と混同しないでください。特に、元の要件は「Java 2プラットフォームで導入されたコレクションAPIには、明確で実証可能な移行パスが必要です」でした。[46]これは、新しい汎用コレクションが、既存のコレクションクラスの1つを期待するメソッドに渡せるように設計されました。[80]
C# ジェネリックは、完全な下位互換性を維持しながら言語に導入されましたが、完全な移行互換性は維持されていませんでした。古いコード (C# 2.0 より前) は、再コンパイルせずに新しいジェネリック対応ランタイムで変更されずに実行されます。移行互換性については、非ジェネリック .NET 1.x コレクションを置き換えるのではなく、補完する新しいジェネリック コレクション クラスとインターフェイスが開発されました。ジェネリック コレクション インターフェイスに加えて、新しいジェネリック コレクション クラスは、可能な場合は非ジェネリック コレクション インターフェイスを実装します。これにより、既存の (非ジェネリック対応) メソッドがコレクションクラスを使用するようにコーディングされている場合、新しいジェネリック コレクションをそれらのメソッドで使用できなくなります。
共変性と反変性
共変性と反変性は両方の言語でサポートされています。Java には、共変性と反変性の両方を使用して単一のジェネリック クラスがメンバーを宣言できる使用サイト バリアンスがあります。C# には、ジェネリック インターフェイスとデリゲートの定義サイト バリアンスがあります。バリアンスはクラスでは直接サポートされていませんが、バリアント インターフェイスの実装を通じてサポートされています。C# には、メソッドとデリゲートの使用サイト共変性のサポートもあります。
関数型プログラミング
閉鎖
クロージャは、そのレキシカルスコープから変数をキャプチャするインライン関数です。
C#は、完全なクロージャセマンティクスを備えた匿名メソッドまたはラムダ式としてクロージャをサポートしています。[85] [86]
Java では、Java 8 が新しい標準になるまで、匿名内部クラスがクロージャをエミュレートするための推奨される方法であり続けます。これはより冗長な構造です。このアプローチは、実際のクロージャと比較していくつかの違いがあり、特に、囲むスコープからの変数へのアクセスがより制御されています。参照できるのは final メンバーのみです。ただし、Java 8 では、現在のスコープを完全に継承し、実際には新しいスコープを導入しないラムダが導入されています。
メソッドへの参照を後で実行するために渡すことができる場合、メソッドがレキシカル スコープ内の変数/パラメーターへの参照を持っている場合にどうするかという問題が発生します。C# クロージャは、レキシカル スコープ内の任意の変数/パラメーターにアクセスできます。Java の匿名内部クラスでは、レキシカル スコープの最終メンバーへの参照のみが許可されているため、開発者はどの変数をどのような状態で使用可能にするかをマークする必要があります (ボックス化が必要になる可能性があります)。
ラムダと式ツリー
C# と Java には、ラムダと呼ばれる特殊なタイプのインラインクロージャがあります。これらは匿名メソッドです。シグネチャと本体はありますが、名前はありません。これらは主に、他のメソッドの呼び出しでローカル関数値引数を指定するために使用され、主に関数型プログラミングに関連する手法です。
C# では、Java とは異なり、ラムダ関数を使用して、式ツリーと呼ばれる特殊なデータ構造を定義できます。ラムダ関数が実行可能関数として認識されるか、データ構造として認識されるかは、コンパイラの型推論と、ラムダ関数が割り当てまたはキャストされる変数またはパラメーターの型によって異なります。ラムダ関数と式ツリーは、統合言語クエリ(LINQ) で重要な役割を果たします。
メタデータ
前処理、コンパイル、パッケージ化
名前空間とファイルの内容
C# では、名前空間はC++の名前空間と似ています。Javaのパッケージ名とは異なり、名前空間はソース ファイルの場所と一切結び付けられていません。Java ソース ファイルの場所がパッケージ ディレクトリ構造を反映することは必ずしも必要ではありませんが、これは従来の構成です。
どちらの言語でもクラスのインポートが可能で ( Java など)、クラスをその名前だけを使って参照することができます。同じ名前のクラスが複数の名前空間やパッケージに存在することがあります。そのようなクラスは、完全修飾名を使うか、または異なる名前を持つ選択されたクラスだけをインポートすることで参照できます。これを行うには、Java では単一のクラス (例: ) をインポートできます。C# では、次の構文を使用して、新しいローカル名でクラスをインポートできます: 。また、 の形式でクラスの特殊化をインポートすることもできます。
import java.util.*import java.util.Listusing Console = System.Consoleusing IntList = System.Collections.Generic.List<int>
どちらの言語にも、クラス内の静的メソッド/フィールドの一部またはすべての短縮名を使用できる静的インポートfoo(bar)構文があります (たとえば、 where をfoo()別のクラスから静的にインポートできます)。C# には静的クラス構文 (Java の静的内部クラスと混同しないでください) があり、クラスには静的メソッドのみが含まれるように制限されます。C# 3.0 では、ユーザーがメソッドを型に静的に追加できるようにする拡張メソッドfoo.bar()が導入されています (たとえば、 where をfoobar()の型で動作するインポートされた拡張メソッドにすることができます)。
Sun Microsystems Java コンパイラでは、ソース ファイル名はその中にある唯一のパブリック クラスと一致する必要がありますが、C# では同じファイルに複数のパブリック クラスを許可しており、ファイル名に制限はありません。C# 2.0 以降では、ソース コードでpartialキーワードを使用して、クラス定義を複数のファイルに分割できます。Java では、パブリック クラスは常に独自のソース ファイルにあります。C# では、ソース コード ファイルと論理ユニットの分離は密接に関連していません。
条件付きコンパイル
Java とは異なり、C# はプリプロセッサ ディレクティブを使用して条件付きコンパイルを実装します。また、特定のコンパイル定数が定義されている場合にのみ呼び出されるメソッドを定義するConditional属性も提供します。このようにして、 DEBUG定数が定義されている場合にのみ評価される メソッドを使用して、アサーションをフレームワーク機能として提供できます。バージョン 1.4 以降、Java はアサーションの言語機能を提供します。これは、デフォルトでは実行時にオフになっていますが、JVM を呼び出すときにまたはスイッチを使用して有効にすることができます。
Debug.Assert()-enableassertions-ea
スレッドと非同期機能
どちらの言語にも、言語構文の一部として スレッド 同期メカニズムが含まれています。
C# のタスクベースの並列処理
.NET Framework 4.0 では、既存のイベント ベースの非同期モデルに代わる新しいタスク ベースのプログラミング モデルが導入されました。API は、タスクとクラスに基づいています。タスクは、構成したり、連鎖させたりすることができます。
Task<T>
慣例により、 Task を返すすべてのメソッドの名前の末尾にAsyncを付ける必要があります。
パブリックスタティッククラスSomeAsyncCode {パブリックスタティックTask <XDocument> GetContentAsync () { HttpClient httpClient = new HttpClient (); return httpClient . GetStringAsync ( " www.contoso.com" ). ContinueWith ( ( task ) => { string responseBodyAsText = task . Result ; return XDocument . Parse ( responseBodyAsText ); }); } }
var t = SomeAsyncCode.GetContentAsync ( ) . ContinueWith (( task ) = > { var xmlDocument = task.Result ; } ) ;
t .開始();
C# 5 では、タスク モデルの操作を容易にするために、一連の言語およびコンパイラ拡張機能が導入されました。これらの言語拡張機能には、プログラム フローを同期的に見せるためのasyncメソッドとawaitステートメントの概念が含まれています。
パブリック静的クラスSomeAsyncCode {パブリック静的非同期Task <XDocument> GetContentAsync ( ) { HttpClient httpClient = new HttpClient ( );文字列responseBodyAsText = await httpClient.GetStringAsync ( " www.contoso.com " ) ; return XDocument.Parse ( responseBodyAsText ) ; } }
var xmlDocument = SomeAsyncCode.GetContentAsync ()を待機します。
// タスクは await で呼び出されると開始されます。
この構文糖衣から、 C# コンパイラは、開発者が考えなくても必要な継続を処理するステート マシンを生成します。
Java のタスクベースの並列処理
Java は JDK 1.0 以降、スレッドをサポートしています。Java は、タスクと呼ばれることが多いスレッドを実行するための高い汎用性を提供します。これは、java.lang.Runnable次の例に示すように、単一の void no-args メソッドを定義する関数インターフェイス (インターフェイス) を実装することによって実現されます。
var myThread =新しいスレッド(() -> {
var threadName =スレッド.currentThread (). getName ( );
システム.out.println ( " Hello " + threadName ) ;
});
myThreadを開始します();
C# と同様に、Java にはスレッドを操作するためのより高レベルのメカニズムがあります。Executorは非同期タスクを実行でき、サブプロセスのグループも管理できます。ExecutorServices インスタンスのすべてのスレッドはプールで処理されます。このExecutorServiceインスタンスは関連するタスクのために内部で再利用されるため、単一の Executor サービス インスタンスを使用して、アプリケーションのライフサイクル全体を通じてプログラマーが望む数の同時タスクを実行できます。
executor を使用した最初のスレッドの例は次のようになります。
ExecutorService executor = Executors.newSingleThreadExecutor ( ) ;
実行者.submit (() - > {
var threadName =スレッド.currentThread (). getName ( );
システム.out.println ( " Hello " + threadName ) ;
});
ExecutorServiceインスタンスは、 Runnableのような別の単一メソッド インターフェイスであるCallableインターフェイスもサポートしますが、 Callableに含まれるメソッドのシグネチャは値を返します。このように、ラムダ式も値を返す必要があります。以下の C# の例のように、Web サイトを非同期的に呼び出す例がこれに該当します。
ExecutorService executor = Executors.newSingleThreadExecutor ( ) ;
Future <文字列> contentAsync = executor . submit (() -> {
HttpRequest httpReq = HttpRequest.newBuilder ( )
. uri (新しいURI ( "www.graalvm.org " ))
。建てる();
HttpClientを返します。newHttpClient ( )
.send ( httpReq , BodyHandlers.ofString ())を送信します。
。体();
});
var webPageResult = contentAsync 。得る();
メソッドを呼び出すと、get()現在のスレッドがブロックされ、呼び出し可能オブジェクトが完了するまで待機してから値 (この例では、Web ページのコンテンツ) が返されます。
次の例では、メソッドとクラスが使用されています。Java にはメソッド シグネチャ用の async などのキーワードがないため、これをラップするのは C# の例に似せるためです。
パブリック静的クラスSomeAsyncCode {
静的ExecutorService executor = Executors.newSingleThreadExecutor ( ) ;
パブリック静的Future <文字列> getContentAsync (){
Executorを返します。submit ( () -> {
HttpRequest httpReq = HttpRequest.newBuilder ( )
. uri (新しいURI ( "www.graalvm.org " ))
。建てる();
HttpClientを返します。newHttpClient ( )
.send ( httpReq , BodyHandlers.ofString ())を送信します。
。体();
});
}
}
var webPageResult = SomeAsyncCode 。getContentAsync ()。得る();
追加機能
数値アプリケーション
数学や金融計算の分野でのアプリケーションを適切にサポートするために、いくつかの言語機能が存在する。[93]
Java のstrictfpキーワードは、コード領域で厳密な浮動小数点計算を可能にします。厳密な浮動小数点計算では、プラットフォームが計算中に高い精度を提供する場合でも、中間結果を単精度/倍精度に変換する必要があります。これにより、厳密な浮動小数点計算はすべてのプラットフォームでまったく同じ結果を返します。厳密な浮動小数点がない場合、プラットフォーム実装は計算中に中間結果により高い精度を自由に使用できます。C# では、特定のハードウェア アーキテクチャの実装で、中間結果に常により高い精度を使用できます (使用可能な場合)。つまり、C# では、プログラマがオプションで中間結果に単精度/倍精度の潜在的な低い精度を使用するように強制することはできません。[94]
Java の浮動小数点演算は主に IEEE 754 (2 進浮動小数点演算の標準) に基づいていますが、例外フラグや方向付き丸めなど、IEEE 標準 754 で義務付けられている機能の一部は、strictfp 修飾子を使用する場合でもサポートされません ( 「Java の批判」、「浮動小数点演算」を参照)。
C#には組み込みの10進数型[95]があり、これはJava/C#のdoubleよりも精度が高い(ただし範囲は狭い)です。10進数型は128ビットのデータ型で、金融や通貨の計算に適しています。10進数型は、28~29桁の有効桁数で、1.0 × 10 −28から約7.9 × 10 28までの範囲の値を表現できます。[96]この構造ではC#の演算子オーバーロードが使用されているため、他のプリミティブデータ型と同様に、 +、-、*、/などの演算子を使用して小数を操作できます。
BigDecimalJava で提供されるおよび型BigIntegerでは、それぞれ 10 進数と整数を任意精度で表現できます。Java 標準ライブラリには、複素数を扱うクラスはありません。
BigIntegerC#で提供される、[3]、Complex[97]型は、それぞれ任意精度の整数と複素数の表現と操作を可能にします。これらの構造はC#の演算子オーバーロードを使用するため、他のプリミティブデータ型と同様に、 +、-、*、/などの演算子を使用してインスタンスを操作できます。C#標準ライブラリには、任意精度の浮動小数点数を処理するクラスはありません(任意精度の算術演算ソフトウェアを参照)。
C#では、コード領域での
算術オーバーフローの実行時チェックを有効または無効にできるcheckedand演算子を使用して、数学アプリケーションを支援できます。unchecked
言語統合クエリ (LINQ)
C# の統合言語クエリ(LINQ) は、言語内クエリ機能を可能にするために連携して動作するように設計された機能セットであり、C# と Java を区別する機能です。
LINQ は次の機能で構成されています。
- 拡張メソッドを使用すると、既存のインターフェースまたはクラスを新しいメソッドで拡張できます。実装は共有することも、インターフェースに専用の実装を持たせることもできます。
- ラムダを使用すると、基準を関数的に表現できます。
- 式ツリーを使用すると、特定の実装で、実行可能なブロックではなく、抽象構文ツリーとしてラムダをキャプチャできます。実装では、これを利用することで、たとえばLinq、LINQ to SQLの場合のように、SQL where 句の形式で、異なる言語で基準を表すことができます。
- 匿名型と型推論は、クエリの結果の型のキャプチャと操作をサポートします。クエリは、名前を付けることができない結果の型につながる可能性があるクエリ ソースを結合および投影する場合があります。
- SQLユーザーに馴染みのある構文をサポートするクエリ式。
- null 許容型 (リフトされた) 型により、 SQLなどの null 許容型をサポートするクエリ プロバイダーとの適合性が向上します。
ネイティブ相互運用性
Java Native Interface (JNI) 機能を使用すると、Java プログラムから非 Java コードを呼び出すことができます。ただし、JNI では、呼び出されるコードがいくつかの規則に従う必要があり、使用される型と名前に制限が課せられます。つまり、レガシー コードと Java の間に追加の適応レイヤーが必要になることがよくあります。この適応コードは、Java 以外の言語 (多くの場合、C または C++) でコーディングする必要があります。Java Native Access (JNA) を使用すると、Java コードの記述のみでネイティブ コードを簡単に呼び出すことができますが、パフォーマンス コストがかかります。
さらに、サードパーティのライブラリでは、JACOB (無料) や J-Integra for COM (独自仕様) などのJavaコンポーネント オブジェクト モデル(COM) ブリッジングが提供されています。
.NET プラットフォーム Invoke ( P/Invoke ) は、C# から Microsoft がアンマネージコードと呼ぶものへの呼び出しを許可することで、同じ機能を提供します。メタデータ属性を通じて、プログラマーはパラメーターと結果のマーシャリング方法を正確に制御できるため、Java の同等の JNI に必要な外部グルー コードを回避できます。P/Invoke では、手続き型 API (Win32 や POSIX など) へのほぼ完全なアクセスが可能ですが、C++ クラス ライブラリへのアクセスは制限されます。
さらに、.NET Framework は .NET-COM ブリッジも提供しており、COM コンポーネントにファーストクラスの .NET オブジェクトであるかのようにアクセスできるようになります。
C# では、プログラマがCLRの通常の型チェックやその他の安全機能を無効にして、ポインタ変数を使用できるようにすることもできます。この機能を使用する場合、プログラマはunsafeキーワードを使用してコードをマークする必要があります。JNI、P/Invoke、および「unsafe」コードは、セキュリティ ホールやアプリケーションの不安定性を引き起こす可能性のある、同様に危険な機能です。P/Invoke や JNI よりも unsafe なマネージド コードの利点は、プログラマが使い慣れた C# 環境で作業を続け、通常は unmanaged コードを呼び出す必要があるタスクを実行できることです。unsafe コードを使用するアセンブリ (プログラムまたはライブラリ) は、特別なスイッチを使用してコンパイルする必要があり、そのようにマークされます。これにより、ランタイム環境では、潜在的に有害なコードを実行する前に特別な予防措置を講じることができます。
ランタイム環境
Java (プログラミング言語) は、 Java ランタイム環境(JRE) を介して Java プラットフォーム上で実行されるように設計されています。Java プラットフォームには、Java 仮想マシン(JVM) と共通のライブラリ セットが含まれています。JRE は元々、最終的なコンパイルをオプションとして、解釈実行をサポートするように設計されていました。ほとんどの JRE 環境では、完全にまたは少なくとも部分的にコンパイルされたプログラムが、適応型最適化を使用して実行されます。Java コンパイラはJava バイトコードを生成します。実行時に、バイトコードは Java ランタイムによってロードされ、直接解釈されるか、マシン命令にコンパイルされてから実行されます。
C# は、共通言語ランタイム(CLR) 上で実行されるように設計されています。CLR は、完全にコンパイルされたコードを実行するように設計されています。C# コンパイラは、共通中間言語命令を生成します。実行時に、ランタイムはこのコードを読み込み、ターゲット アーキテクチャ上のマシン命令にコンパイルします。
例
入力/出力
両方の言語を使用して、あるファイルから別のファイルにテキストを 1 行ずつコピーする方法を示した例。
ライブラリ定義型の統合
C# では、次の例に示すように、カスタムの暗黙的/明示的な変換と演算子のオーバーロードを使用して、ライブラリ定義の型を既存の型や演算子と統合できます。
C# デリゲートと同等の Java 構造
タイプリフティング
動的言語との相互運用性
この例では、Java と C# を使用して、別のプログラミング言語で実装されたクラスのインスタンスを作成し、呼び出す方法を示します。「Deepthought」クラスはRubyプログラミング言語を使用して実装されており、Calculate メソッドが呼び出されたときに 2 つの入力値 ( aとb )を乗算する単純な計算機を表します。従来の方法に加えて、Java には実装された任意のプログラミング言語を実行できる仮想マシン であるGraalVMがあります。
フィボナッチ数列
この例では、2 つの言語を使用してフィボナッチ数列を実装する方法を示します。C# バージョンでは、C# ジェネレーター メソッドを活用します。Java バージョンでは、Streamインターフェイスとメソッド参照を活用します。Java と C# の両方の例では、クラス、メソッド、およびステートメントのコード フォーマットにK&R スタイルを使用します。
参照
参考文献
- ^ 「BigDecimal (Java 2 Platform SE 5.0)」。Docs.oracle.com。2015年2 月 24 日閲覧。
- ^ 「Mpir.NET」 。 2015年7月17日閲覧。
- ^ ab "BigInteger Struct (System.Numerics)". learn.microsoft.com . 2023 年4 月 14 日閲覧。
- ^ 「Collection (Java 2 Platform SE 5.0)」。Docs.oracle.com。2015年5 月 20 日閲覧。
- ^ 「String (Java 2 Platform SE 5.0)」。Docs.oracle.com。2015年5 月 20 日閲覧。
- ^ 「数学 - The Commons Math User Guide - 複素数」。2015年7月17日閲覧。
- ^ 「Date (Java 2 Platform SE 5.0)」。Docs.oracle.com。2015年5 月 20 日閲覧。
- ^ 「decimal (C# リファレンス)」。Microsoft。2015年11 月 30 日閲覧。
- ^ abc 「Java 言語環境」。Oracle.com。2013年8 月 18 日閲覧。
- ^ ab 「メソッド参照 (Java チュートリアル > Java 言語の学習 > クラスとオブジェクト)」。Docs.oracle.com。2012 年 2 月 28 日。2015年2 月 24 日に閲覧。
- ^ 安全でないモードまたはIntPtr管理型を通じてのみ使用可能
- ^ 型システムは、コンパイラが生のポインタをデータ型として使用できるアンセーフモードに切り替えられない限り、デフォルトで統合されています。ポインタはオブジェクトから派生されておらず、オブジェクトデータ型との間で暗黙的な変換もありません。
- ^ "org.apache.commons.lang3.tuple (Apache Commons Lang 3.4-SNAPSHOT API)". Commons.apache.org. 2014年1月15日. 2015年2月24日閲覧。
- ^ ab "Tuple クラス (システム)"。learn.microsoft.com。Microsoft Corporation。2023年4 月 20 日閲覧。
- ^ ab 「タプル型 (C# リファレンス)」。learn.microsoft.com。Microsoft Corporation。2023年4 月 20 日閲覧。
- ^ 「Unsigned Integer Arithmetic API now in JDK 8 (Joseph D. Darcy's Oracle Weblog)」。Blogs.oracle.com。2017年2月25日時点のオリジナルよりアーカイブ。2015年2月24日閲覧。
- ^ 「アンセーフ コード、ポインター型、関数ポインター」。learn.microsoft.com。2022 年 5 月 29 日。2023年4 月 14 日に閲覧。
- ^ Joshua Bloch、Neal Gafter (2005)。Javaのパズル:罠、落とし穴、コーナーケース(第 5 版)。アッパーサドルリバー、ニュージャージー州 [ua]:Addison-Wesley。p. 36。ISBN 978-0-321-33678-1
言語設計者にとっての教訓は、バイト値の符号拡張がバグや混乱の一般的な原因であるということです。符号拡張を抑制するために必要なマスクによりプログラムが乱雑になり、読みにくくなります。したがって、バイト型は符号なしにする必要があります
。{{cite book}}: CS1 maint: multiple names: authors list (link) - ^ 「James Gosling on Java、2001年5月」。Artima.com、2001年5月10日。 2015年2月24日閲覧。
- ^ 「Integer (Java Platform SE 8)」。Docs.oracle.com 。 2023年4月20日閲覧。
- ^ "decimal". C# リファレンス. Microsoft. 2012 年 8 月 19 日.
- ^ ab Sestoft、Jon Jagger 、 Nigel Perry、Peter (2007)。「11.1.7 小数点型」。C ? 注釈付き標準。アムステルダム: Elsevier/Morgan Kaufmann Publishers。ISBN 978-0-12-372511-0。
{{cite book}}: CS1 maint: multiple names: authors list (link) - ^ Mok, Heng Ngee (2003). 「9.5. 小数点型」. Java から C へ?: 開発者ガイド. ハーロー、イギリス: Addison-Wesley. ISBN 978-0-321-13622-0。
- ^ 「パッケージ java.time (Java Platform SE 8)」。docs.oracle.com 。 2023年4月20日閲覧。
- ^ 「DateOnly および TimeOnly 構造体の使用方法」。learn.microsoft.com。2023 年 1 月 12 日。2023年4 月 20 日に閲覧。
- ^ "Enum". インターネット: .NET Perls 。2016 年11 月 14 日閲覧。
パフォーマンス。
列挙型は高速です。パフォーマンスが問題になることはほとんどありません。列挙型は、同じく高速な int などの型の糖衣構文にすぎません。[…]
型。
列挙型には基になる型があります。列挙型を使用するたびに、基になる型を使用しています。列挙型には糖衣構文があります。
- ^ ab Dare Obasanjo (2007). 「Microsoft の C# プログラミング言語と Sun Microsystems の Java プログラミング言語の比較」 Dare Obasanjo. 2001 年 12 月 17 日時点のオリジナルよりアーカイブ。2012年9 月 6 日閲覧。Java
では、列挙型は完全なクラスであり、タイプセーフであり、メソッド、フィールドを追加したり、インターフェイスを実装したりして拡張できます。一方、C# では、列挙型は整数型 (通常は int) の単なる構文糖であり、拡張できず、タイプセーフでもありません。
- ^ グランツ博士、ドミニク教授 (2005 年 4 月 8 日)。 「Java 5: Taming the Tiger: Syntactic Sugar」 (ドイツ語)。アールガウ大学、ノルドヴェストシュヴァイツ。 2012 年 7 月 8 日のオリジナルからアーカイブ。2012 年9 月 10 日に取得。
Java 1.5 に関する情報を列挙します。 Nach vielen Beteuerungen durch Sun, Enums seien in Java überflüssig und können einfach nachgebildet werden, wurden sie nun doch eingefüult. Die einfachste Möglichkeit einer Enumeration der Jahreszeiten sieht wie folgt aus … Das Schlüsselwort enum steht für eine spezielle Art von Klasse, die eine Enumeration definiert. …
私は、C/C++ と C# に関するプログラムの開発を行っており、Gleichheitszeichen の重要性を理解しながら、Zahlen zuordnen を理解しています。
- ^ Dare Obasanjo (2007)。「Microsoft の C# プログラミング言語と Sun Microsystems の Java プログラミング言語の比較: C. ほんの少しの既視感: 4. Switch ステートメント」。Dare Obasanjo。2012 年 9 月 19 日時点のオリジナルよりアーカイブ。2012年9 月 10 日閲覧。
- ^ "goto (C#)". Msdn.microsoft.com . 2013 年8 月 18 日閲覧。
- ^ ab 「Oracle Technology Network for Java Developers | Oracle Technology Network | Oracle」。Java.sun.com。2012年6月27日時点のオリジナルよりアーカイブ。2015年2月24日閲覧。
- ^ Swing アプリケーションで JavaFX を使用する方法 2009 年 3 月 4 日にWayback Machineにアーカイブされました
- ^ 「アンセーフ コードとポインター (C# プログラミング ガイド)」。Microsoft。2013年3 月 11 日閲覧。
- ^ 「SortedDictionary<TKey,TValue> クラス (System.Collections.Generic)」。learn.microsoft.com。2023年4 月 20 日閲覧。
- ^ 「SortedSet<T> クラス (System.Collections.Generic)」。learn.microsoft.com。2023年4 月 20 日閲覧。
- ^ 「PriorityQueue<TElement,TPriority> クラス (System.Collections.Generic)」。learn.microsoft.com。2023年4 月 20 日閲覧。
- ^ 「System.Collections.Concurrent 名前空間」。learn.microsoft.com 。2023年4 月 20 日閲覧。
- ^ "foreach, in (C# リファレンス)". Microsoft. 2018. 2019 年 1 月 12 日時点のオリジナルからアーカイブ。2019 年1 月 26 日に取得。 foreach ステートメントは、
System.Collections.IEnumerable
または
System.Collections.Generic.IEnumerable<T>
インターフェイスを実装する型のインスタンス内の各要素に対して、ステートメントまたはステートメントのブロックを実行します
。
- ^ Dare Obasanjo (2007). 「Microsoft の C# プログラミング言語と Sun Microsystems の Java プログラミング言語の比較: C。ほんの少しの既視感: 6. コレクション」。 Dare Obasanjo。 2012 年 9 月 19 日のオリジナルからのアーカイブ。 2012 年9 月 10 日閲覧。
Java コレクション フレームワークには、スレッド セーフな方法で安全でないコレクションにアクセスできるメソッドだけでなく、ほとんどのデータ構造のスレッド セーフ バージョンも含まれています。 Java コレクション フレームワークには、データ構造内の要素を操作するためのアルゴリズムが多数あり、その中には、コンパレータに基づいて最大要素を検索する、最小要素を検索する、リスト内のサブリストを検索する、リストの内容を反転する、リストの内容をシャッフルする、コレクションの不変バージョンを作成する、ソートを実行する、バイナリ検索を実行するなどのアルゴリズムがあります。
- ^ Dare Obasanjo (2007 年 3 月)。「Microsoft の C# プログラミング言語と Sun Microsystems の Java プログラミング言語の比較: C。ほんの少しの既視感: 6. コレクション」。Dare Obasanjo。2013 年 1 月 2 日時点のオリジナルよりアーカイブ。2012年9 月 10 日閲覧。C
# コレクション フレームワークは、System.Collections および System.Collections.Generic 名前空間のクラスで構成されます。Systems.Collections
名前
空間には
、IList、IEnumerable、IDictionary、ICollection
、
CollectionBase
などの抽象データ型を表す
インターフェイスと抽象クラスが含まれており、開発者は、データ構造が抽象データ型から継承されている限り、実際の実装方法に関係なくデータ構造を操作できます。System.Collections 名前空間には
、ArrayList、Stack、Queue、HashTable
、
SortedList
などのデータ構造の具体的な実装も含まれています
。 4 つの具体的なデータ構造実装はすべて、スレッドセーフな方法でアクセスできるコレクションへの同期ラッパーを取得できるようにします。System.Collections.Generic名前空間には、汎用の
List<T>
、
Stack<T>
、
Queue<T>
、
Dictionary<K,T>
、および
SortedDictionary<K,T>クラスを含む、
System.Collections
名前空間の主要なデータ構造の汎用実装があります
。
- ^ "javatuples" . 2023年4月20日閲覧。
- ^ 「$ を使用した文字列補間」。learn.microsoft.com。2023 年 4 月 8 日。2023 年4 月 20 日に閲覧。
- ^ 「JEP 378: テキストブロック」 。 2020年8月5日閲覧。
- ^ Dare Obasanjo (2007)。「Microsoft の C# プログラミング言語と Sun Microsystems の Java プログラミング言語の比較: D. 今度はまったく違う話: 13. 逐語的文字列」。Dare Obasanjo。2012 年 9 月 19 日時点のオリジナルよりアーカイブ。2012年9 月 11 日閲覧。
- ^ Eric Fleegal (2004). 「Microsoft Visual C++ 浮動小数点最適化」. MSDN . 2016 年1 月 1 日閲覧。
- ^ ab 「Java Community Process(SM) プログラム – JSR: Java 仕様要求 – 詳細 JSR# 14」。Jcp.org。2015年2 月 24 日閲覧。
- ^ 「JEP 286: ローカル変数型推論」。2018年4月25日閲覧。
- ^ Dare Obasanjo (2007)。「Microsoft の C# プログラミング言語と Sun Microsystems の Java プログラミング言語の比較: D. 今度はまったく違うもの: 14. オーバーフロー検出」。2012 年 9 月 22 日時点のオリジナルよりアーカイブ。2012年9 月 11 日閲覧。
- ^ Dare Obasanjo (2007). 「Microsoft の C# プログラミング言語と Sun Microsystems の Java プログラミング言語の比較: ほんの少しの既視感: 4. switch ステートメント [sic]」。2012 年 9 月 22 日時点のオリジナルよりアーカイブ。2012年9 月 7 日閲覧。
- ^ 「try-with-resources ステートメント (Java チュートリアル > 基本クラス > 例外)」。Docs.oracle.com。2012 年 2 月 28 日。2015年2 月 24 日に取得。
- ^ Javaプログラミング言語用に作成された拡張機能
- ^ 「匿名型 (C# の基礎)」。learn.microsoft.com。2013年4 月 14 日閲覧。
- ^ 「Java SE 仕様」Java.sun.com 。2015年2 月 24 日閲覧。
- ^ Dare Obasanjo (2007). 「Microsoft の C# プログラミング言語と Sun Microsystems の Java プログラミング言語の比較: 演算子のオーバーロード」。 Dare Obasanjo。 2012 年 9 月 19 日時点のオリジナルよりアーカイブ。 2012 年9 月 6 日閲覧。
注: C++ とは異なり、C# では、
new、( )、||、&&、= 、または
+=、-=
などの複合代入のバリエーションの演算子のオーバーロードは許可されません。
ただし、複合代入演算子はオーバーロードされた演算子を呼び出します。たとえば、
+= はオーバーロードされた
+
を呼び出します
。
- ^ 「1998年8月のJavaニュース」Cafeaulait.org 。 2015年2月24日閲覧。
- ^ Sunwold, Corey (2010 年 2 月 25 日)。「C# の Java の "final" に相当するもの」。Corey Sunwold。2012 年 11 月 29 日のオリジナルからアーカイブ。2016年9 月 13 日取得。C
# には同等のものがない final キーワードの使用法が 1 つ以上あります。Java でメソッドにパラメータを渡し、そのメソッドのスコープ内でそのパラメータの値を変更したくない場合は、次のように final として設定できます。
- ^ 「C# – AC# 6.0 言語プレビュー」。learn.microsoft.com。2015 年 7 月。2023 年4 月 14 日に閲覧。
- ^ ab Dare Obasanjo (2007). 「Microsoft の C# プログラミング言語と Sun Microsystems の Java プログラミング言語の比較: D. 今度は完全に異なる点: 15. 明示的なインターフェイスの実装」 Dare Obasanjo. 2012 年 9 月 22 日時点のオリジナルよりアーカイブ。2012年9 月 11 日閲覧。
- ^ "in パラメーター修飾子 (C# リファレンス)". Microsoft. 2018 年 3 月 5 日. 2019 年 1 月 26 日時点のオリジナルよりアーカイブ。 2019 年1 月 26 日閲覧。
- ^ Gosling, James. 「Java® 言語仕様」セクション 8.4.1. 形式パラメータ。2014年10 月 5 日閲覧。
{{cite web}}: CS1 maint: location (link) - ^ Hanselman, Scott (2008 年 4 月 4 日)。「拡張メソッドはどのように機能し、なぜ新しい CLR が必要なかったのか?」。2014 年3 月 29 日閲覧。
拡張メソッドは、実に優れた構文糖です。ご覧のとおり、クラスに実際に追加されるわけではありませんが、コンパイラは、それがクラスに追加されているかのように感じさせます。
- ^ 「拡張メソッド (C# プログラミング ガイド)」。Microsoft。2013年。2014 年3 月 29 日閲覧。
拡張メソッドは静的メソッドとして定義されますが、インスタンス メソッド構文を使用して呼び出されます。
- ^ 「C# 言語仕様バージョン 4.0」。Microsoft。p. 281。2012年5 月 10 日に取得。
部分型宣言のどの部分にも、特定の部分メソッドの実装宣言が含まれていない場合、その部分メソッドを呼び出す式ステートメントは、結合型宣言から単に削除されます。したがって、呼び出し式 (構成式を含む) は、実行時に効果がありません。部分メソッド自体も削除され、結合型宣言のメンバーにはなりません。特定の部分メソッドの実装宣言が存在する場合、部分メソッドの呼び出しは保持されます。部分メソッドは、次の点を除いて、実装部分メソッド宣言に似たメソッド宣言を生成します。[…]
- ^ "in パラメータ修飾子 (C# リファレンス)". Microsoft. 2018 年 3 月 5 日。2019 年 1 月 26 日時点のオリジナルよりアーカイブ。2019年1 月 26 日取得。in
キーワードは、引数が参照渡しされる原因となります。これは ref キーワードや out キーワードに似ていますが、in 引数は呼び出されたメソッドによって変更できない点が異なります。
- ^ Dare Obasanjo (2007). 「Microsoft の C# プログラミング言語と Sun Microsystems の Java プログラミング言語の比較: D. 今度は完全に異なるもの: 12. 参照渡し」 Dare Obasanjo. 2012 年 9 月 19 日のオリジナルからアーカイブ。2012年9 月 10 日取得。Java
では、メソッドの引数は値渡しされるため、メソッドは実際のアイテムではなく、渡されたアイテムのコピーに対して動作します。C# では、C++ やある意味では C と同様に、メソッドの引数が実際にはメソッドに渡されるアイテムのコピーではなく参照になるように指定できます。この機能は、複数のオブジェクトを返すメソッドを作成する場合に特に便利です。Java では、メソッドから複数の値を返そうとすることはサポートされておらず、次のような異常が発生します。長年、新入生のコンピュータ サイエンスの授業で特徴的だった 2 つの数値を交換するメソッドは、コーディングのトリックに頼らなければ Java では実行できません。
- ^ 「例外的な例外フィルタリング」。Pluralsight® 。 2022年6月24日閲覧。
- ^ 「チェック例外のトラブル」 Artima.com 。2015年2 月 24 日閲覧。
- ^ 「Msdn フォーラム – Visual C# 言語」。Msdn2.microsoft.com。2007 年 3 月 20 日時点のオリジナルよりアーカイブ。2015 年2 月 24 日閲覧。
- ^ Eckel, Bruce. 「Java にはチェック例外が必要ですか?」。2002 年 4 月 5 日時点のオリジナルよりアーカイブ。2012年12 月 6 日閲覧。
- ^ 「失敗と例外」 Artima.com 2003年9月22日。 2013年8月18日閲覧。
- ^ 「チェック例外」。Shaun Abram 。 2013年8月18日閲覧。
- ^ 「Java SE 仕様」Java.sun.com 。2015年2 月 24 日閲覧。
- ^ Angelika Langer. 「Java Generics FAQs – よくある質問 – Angelika Langer トレーニング/コンサルティング」。AngelikaLanger.com。2015年2 月 24 日閲覧。
- ^ Angelika Langer (2013 年 4 月 16 日)。「Java Generics FAQs – Under The Hood of the Compiler – Angelika Langer Training/Consulting」。AngelikaLanger.com。2013年8 月 18 日閲覧。
- ^ Angelika Langer (2013 年 4 月 16 日)。「Java Generics FAQs – Under The Hood of the Compiler – Angelika Langer Training/Consulting」。AngelikaLanger.com。2013年8 月 18 日閲覧。
- ^ Angelika Langer (2013 年 4 月 16 日)。「Java Generics FAQs – Under The Hood of the Compiler – Angelika Langer Training/Consulting」。AngelikaLanger.com。2013年8 月 18 日閲覧。
- ^ Angelika Langer (2014 年 2 月 13 日)。「Java Generics FAQs – Type Parameters – Angelika Langer Training/Consulting」。AngelikaLanger.com。2015年2 月 24 日閲覧。
- ^ 「C#、Java、C のジェネリック」 Artima.com 。2015年2 月 24 日閲覧。
- ^ "trove4j / Trove" . 2017年6月30日閲覧。
- ^ Neal Gafter (2004年9月23日). 「Neal Gafterのブログ:Puzzling Through Erasure:回答セクション」Gafter.blogspot.com . 2013年8月18日閲覧。
- ^ 「ラムダ式 (Java チュートリアル > Java 言語の学習 > クラスとオブジェクト)」。Docs.oracle.com。2012 年 2 月 28 日。2015年2 月 24 日に閲覧。
- ^ 「レッスン: 集計操作 (Java チュートリアル > コレクション)」。Docs.oracle.com。2012 年 2 月 28 日。2015年2 月 24 日に閲覧。
- ^ Bruno, Eric (2022年11月7日). 「Curly Braces #6: 再帰と末尾呼び出しの最適化」。2024年7月17日時点のオリジナルよりアーカイブ。2024年7月17日閲覧。
- ^ Grant Richins (2009 年 5 月 11 日)。「.NET Framework 4 での末尾呼び出しの改善」。MSDN ブログ。
- ^ Richter, Jeffrey (2001 年 4 月)。「デリゲートの紹介」。MSDNマガジン。2008 年12 月 23 日閲覧。
- ^ Campbell, Dustin (2007 年 2 月 9 日). 「What's in a Closure?」. Did it with .NET . 2014 年 8 月 15 日時点のオリジナルよりアーカイブ。2008 年12 月 23 日閲覧。
- ^ Dare Obasanjo (2007)。「Microsoft の C# プログラミング言語と Sun Microsystems の Java プログラミング言語の比較: メタデータ注釈」。Dare Obasanjo。2012 年 9 月 19 日のオリジナルからのアーカイブ。2012年9 月 6 日取得。
ただし、C# 属性と Java 注釈の主な違いは、Java ではメタ注釈 (注釈に対する注釈) を作成できますが、C# では同じことはできません。開発者は、@interface キーワードを使用して定義する点を除いてインターフェイスに似た注釈型を作成することで、独自のカスタム注釈を作成できます。
- ^ 「要素」。Msdn.microsoft.com。2013年8 月 18 日閲覧。
- ^ 「C# アセンブリ – カスタム参照パス – Visual C# Kicks」。Vcskicks.com。2013年8 月 18 日閲覧。
- ^ 「Java で条件付きコンパイルを実行する方法」。weblogs.java.net。2013 年 1 月 5 日時点のオリジナルよりアーカイブ。2015年8 月 11 日閲覧。
- ^ Java バージョン 7 に含まれる Fork-Join フレームワーク。「ForkJoinPool (Java Platform SE 7)」。Oracle。2015年7 月 17 日閲覧。
- ^ 「タスク並列ライブラリ (TPL)」。Msdn.microsoft.com。2015 年 2 月 18 日。2015 年2 月 24 日に閲覧。
- ^ 「Java for Scientific Computation: Prospects and Problems」(PDF) 。Pds.ewi.tudelft.nl。 2007年9月22日時点のオリジナル(PDF)からアーカイブ。2015年2月24日閲覧。
- ^ "C# 言語仕様バージョン 5.0". Microsoft. 4.1.6 浮動小数点型. 2013 年10 月 28 日取得。
浮動小数点演算は、演算の結果の型よりも高い精度で実行できます。たとえば、一部のハードウェア アーキテクチャは、double 型よりも範囲と精度が大きい "拡張" または "long double" 浮動小数点型をサポートしており、すべての浮動小数点演算をこの高精度の型を使用して暗黙的に実行します。パフォーマンスを大幅に犠牲にしてのみ、このようなハードウェア アーキテクチャは、より
低い
精度で浮動小数点演算を実行できます。C# では、パフォーマンスと精度の両方を犠牲にする実装を要求するのではなく、すべての浮動小数点演算に高精度の型を使用できます。より正確な結果が得られる以外に、測定可能な効果はほとんどありません。ただし、x*y/z 形式の式では、乗算によって double の範囲外の結果が生成されますが、その後の除算によって一時的な結果が double の範囲に戻ります。この場合、式はより広い範囲の形式で評価されるため、無限大ではなく有限の結果が生成される可能性があります。
- ^ 「decimal vs. 110」 。 2015年2月24日閲覧。[リンク切れ ]
- ^ 「C# 言語仕様バージョン 5.0」。Microsoft。4.1.7 10 進数型。
- ^ 「Complex」 . 2015年2月24日閲覧。[リンク切れ ]
- ^ ab Dare Obasanjo (2007). 「Microsoft の C# プログラミング言語と Sun Microsystems の Java プログラミング言語の比較: C. ほんの少しの既視感: 15. 言語間の相互運用性」 Dare Obasanjo. 2012 年 9 月 19 日時点のオリジナルからのアーカイブ。2012年9 月 10 日閲覧。Java
では、言語間の相互運用性が実現される方法がいくつか存在します。まず、Java Native Interface (JNI) があります。…Java には、Java IDL を介して共通オブジェクト リクエスト ブローカー アーキテクチャ (CORBA) を使用する分散オブジェクトと対話する機能もあります。…C# と .NET ランタイムは、シームレスな言語間の相互運用性を設計目標として作成されました。
- ^ 「JNI タイプとデータ構造」。Docs.oracle.com。2020年4 月 9 日閲覧。
- ^ 「C# 9 の関数ポインター」。docs.microsoft.com。2021年2 月 27 日閲覧。
- ^ 「C# で C/C++ 共用体を作成する」。docs.microsoft.com。2021年2 月 27 日閲覧。
外部リンク
- MSDNでの C# と .NET Framework への移行
- C# と Java: MSDNでのプログラミング言語の比較
- Java と C# – コードの比較
- 9つの言語のパフォーマンス総括
- Microsoft Developer Network (MSDN): Java 開発者向け C# プログラミング言語
- 標準 ECMA-334 C# 言語仕様
- Java 言語仕様 (Sun)
- C# の現状: まだ実行可能な言語ですか?
