「C SharpとJavaの比較」の版間の差分
編集の要約なし |
|||
40行目: | 40行目: | ||
いずれの言語も配列は共変性 (covariance) を持つ。 |
いずれの言語も配列は共変性 (covariance) を持つ。 |
||
< |
<syntaxhighlight lang="csharp"> |
||
System.Console.WriteLine(typeof(int[]).BaseType); // System.Array |
System.Console.WriteLine(typeof(int[]).BaseType); // System.Array |
||
object[] objs = new string[3]; // 代入可能。 |
object[] objs = new string[3]; // 代入可能。 |
||
objs[0] = "test"; |
objs[0] = "test"; |
||
objs[0] = 10; // System.ArrayTypeMismatchException |
objs[0] = 10; // System.ArrayTypeMismatchException |
||
</syntaxhighlight> |
|||
</source> |
|||
< |
<syntaxhighlight lang="java"> |
||
System.out.println(int[].class.getSuperclass()); // java.lang.Object |
System.out.println(int[].class.getSuperclass()); // java.lang.Object |
||
Object[] objs = new String[3]; // 代入可能。 |
Object[] objs = new String[3]; // 代入可能。 |
||
objs[0] = "test"; |
objs[0] = "test"; |
||
objs[0] = 10; // java.lang.ArrayStoreException |
objs[0] = 10; // java.lang.ArrayStoreException |
||
</syntaxhighlight> |
|||
</source> |
|||
なお、Java 1.5で拡張for文がサポートされた。これはC#の<code>[[foreach文|foreach]]</code>文に相当する。[[配列]]を始めとする[[コンテナ (データ型)|コレクション]]型を、[[イテレータ]]により走査することができる。 |
なお、Java 1.5で拡張for文がサポートされた。これはC#の<code>[[foreach文|foreach]]</code>文に相当する。[[配列]]を始めとする[[コンテナ (データ型)|コレクション]]型を、[[イテレータ]]により走査することができる。 |
||
73行目: | 73行目: | ||
C#、Java共に、ジェネリクスの型制約を持つ。C#では基底型指定以外に、<code>class</code>(参照型)、<code>struct</code>(値型)、<code>new()</code>(デフォルトコンストラクタ有)の各制約を指定できる。 |
C#、Java共に、ジェネリクスの型制約を持つ。C#では基底型指定以外に、<code>class</code>(参照型)、<code>struct</code>(値型)、<code>new()</code>(デフォルトコンストラクタ有)の各制約を指定できる。 |
||
< |
<syntaxhighlight lang="csharp"> |
||
// C# |
// C# |
||
class ConstraintClass { } |
class ConstraintClass { } |
||
class SomeGenericsClass<T> where T : ConstraintClass { } |
class SomeGenericsClass<T> where T : ConstraintClass { } |
||
class OtherGenericsClass<T> where T : new() { } |
class OtherGenericsClass<T> where T : new() { } |
||
</syntaxhighlight> |
|||
</source> |
|||
< |
<syntaxhighlight lang="java"> |
||
// Java |
// Java |
||
class ConstraintClass { } |
class ConstraintClass { } |
||
class SomeGenericsClass<T extends ConstraintClass> { } |
class SomeGenericsClass<T extends ConstraintClass> { } |
||
</syntaxhighlight> |
|||
</source> |
|||
==== ジェネリクスの共変性と反変性 ==== |
==== ジェネリクスの共変性と反変性 ==== |
||
90行目: | 90行目: | ||
C#では、型定義側で共変性<code>out</code>あるいは反変性<code>in</code>を指定し、利用側では変性の指定を行わない。なお、C#の値型は不変 (invariant) であり、共変性・反変性は適用されない。 |
C#では、型定義側で共変性<code>out</code>あるいは反変性<code>in</code>を指定し、利用側では変性の指定を行わない。なお、C#の値型は不変 (invariant) であり、共変性・反変性は適用されない。 |
||
< |
<syntaxhighlight lang="csharp"> |
||
// C# |
// C# |
||
// System.Func<T, TResult> と同様のデリゲートを定義。 |
// System.Func<T, TResult> と同様のデリゲートを定義。 |
||
106行目: | 106行目: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
Javaでは、型定義側では変性の指定を行わず、利用側で共変性<code>extends</code>あるいは反変性<code>super</code>を指定する。 |
Javaでは、型定義側では変性の指定を行わず、利用側で共変性<code>extends</code>あるいは反変性<code>super</code>を指定する。 |
||
Javaではさらに、上限と下限のいずれも指定しないワイルドカード<code>?</code>による型引数の指定が可能である。以下にJava 8の例を示す。 |
Javaではさらに、上限と下限のいずれも指定しないワイルドカード<code>?</code>による型引数の指定が可能である。以下にJava 8の例を示す。 |
||
< |
<syntaxhighlight lang="java"> |
||
// Java |
// Java |
||
// java.util.function.Function<T, R> と同様のインターフェイスを定義。 |
// java.util.function.Function<T, R> と同様のインターフェイスを定義。 |
||
131行目: | 131行目: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
=== 型推論 === |
=== 型推論 === |
||
161行目: | 161行目: | ||
|- |
|- |
||
| using, try || C#では、<code>using</code>ステートメントによって、作成されたオブジェクトがブロックを抜ける際に確実に破棄されるよう強制することができる。 |
| using, try || C#では、<code>using</code>ステートメントによって、作成されたオブジェクトがブロックを抜ける際に確実に破棄されるよう強制することができる。 |
||
< |
<syntaxhighlight lang="csharp"> |
||
// "test.txt"というファイルを作成し、文字列を書き込み、(例え例外が発生しても)確実に閉じる。 |
// "test.txt"というファイルを作成し、文字列を書き込み、(例え例外が発生しても)確実に閉じる。 |
||
using (StreamWriter file = new StreamWriter("test.txt")) |
using (StreamWriter file = new StreamWriter("test.txt")) |
||
167行目: | 167行目: | ||
file.Write("test"); |
file.Write("test"); |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
Java 7では、try-with-resources構文で同様のリソース解放に対応している。 |
Java 7では、try-with-resources構文で同様のリソース解放に対応している。 |
||
|- |
|- |
||
| goto || C#では[[goto文]]がサポートされる。これは便利な場面もあるが、通常はより構造化されたフロー制御方法が推奨される。C#では<code>switch</code>ステートメントにおいて<code>goto</code>キーワードを使うことで、異なる<code>case</code>ラベルに移ることができる。 |
| goto || C#では[[goto文]]がサポートされる。これは便利な場面もあるが、通常はより構造化されたフロー制御方法が推奨される。C#では<code>switch</code>ステートメントにおいて<code>goto</code>キーワードを使うことで、異なる<code>case</code>ラベルに移ることができる。 |
||
< |
<syntaxhighlight lang="csharp"> |
||
switch (color) |
switch (color) |
||
{ |
{ |
||
178行目: | 178行目: | ||
// ... |
// ... |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
Javaではgoto文はサポートされない。ただし<code>goto</code>は予約されており、識別子として利用できない。代わりに制限されたgotoとして、ラベル付き[[break文]]/[[continue文]]を使うことができる。これにより多重ループの脱出などを簡潔に記述できる。 |
Javaではgoto文はサポートされない。ただし<code>goto</code>は予約されており、識別子として利用できない。代わりに制限されたgotoとして、ラベル付き[[break文]]/[[continue文]]を使うことができる。これにより多重ループの脱出などを簡潔に記述できる。 |
||
|- |
|- |
||
| yield || C#ではコンテキストキーワード<code>yield</code>が用意されており、<code>yield return</code>文と<code>yield break</code>文を使用して[[イテレータ]](コルーチン)を記述できる。これらの文を含むメソッド(イテレータ)の戻り値はIEnumerableインターフェイス、またはIEnumeratorインターフェイスでなければならない。 |
| yield || C#ではコンテキストキーワード<code>yield</code>が用意されており、<code>yield return</code>文と<code>yield break</code>文を使用して[[イテレータ]](コルーチン)を記述できる。これらの文を含むメソッド(イテレータ)の戻り値はIEnumerableインターフェイス、またはIEnumeratorインターフェイスでなければならない。 |
||
また、イテレータは、内部的に状態を持つ[[ステートマシン]]としてコンパイルされ、値を部分的に返却する列挙子を提供するように振る舞う。 |
また、イテレータは、内部的に状態を持つ[[ステートマシン]]としてコンパイルされ、値を部分的に返却する列挙子を提供するように振る舞う。 |
||
< |
<syntaxhighlight lang="csharp"> |
||
// 指定された個数の乱数を生成する |
// 指定された個数の乱数を生成する |
||
public IEnumerable<int> RandomValueGenerator(int count) |
public IEnumerable<int> RandomValueGenerator(int count) |
||
193行目: | 193行目: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
|} |
|} |
||
219行目: | 219行目: | ||
==== 演算子多重定義 ==== |
==== 演算子多重定義 ==== |
||
Javaは言語仕様をシンプルに保つため、また乱用による難読化を防ぐため、ユーザー定義の演算子オーバーロードをサポートしていない。一方でコードの書きやすさおよび直感性は犠牲になっている。 |
Javaは言語仕様をシンプルに保つため、また乱用による難読化を防ぐため、ユーザー定義の演算子オーバーロードをサポートしていない。一方でコードの書きやすさおよび直感性は犠牲になっている。 |
||
< |
<syntaxhighlight lang="java"> |
||
var myList = new java.util.ArrayList<Integer>(java.util.Collections.nCopies(10, 0)); |
var myList = new java.util.ArrayList<Integer>(java.util.Collections.nCopies(10, 0)); |
||
myList.set(4, -100); |
myList.set(4, -100); |
||
227行目: | 227行目: | ||
gravityTable.put("Earth", 1.0); |
gravityTable.put("Earth", 1.0); |
||
double gravityOnVenus = gravityTable.get("Venus"); |
double gravityOnVenus = gravityTable.get("Venus"); |
||
</syntaxhighlight> |
|||
</source> |
|||
< |
<syntaxhighlight lang="java"> |
||
var b1 = new java.math.BigInteger(Long.toString(Long.MAX_VALUE)); |
var b1 = new java.math.BigInteger(Long.toString(Long.MAX_VALUE)); |
||
var b2 = new java.math.BigInteger(Integer.toString(1)); |
var b2 = new java.math.BigInteger(Integer.toString(1)); |
||
var b3 = b1.add(b2); |
var b3 = b1.add(b2); |
||
</syntaxhighlight> |
|||
</source> |
|||
C#は表記法の多くの点でJavaよりも多機能である。演算子多重定義やユーザー定義キャストなど、それらの多くはC++プログラマによって既に親しまれているものである。 |
C#は表記法の多くの点でJavaよりも多機能である。演算子多重定義やユーザー定義キャストなど、それらの多くはC++プログラマによって既に親しまれているものである。 |
||
C#は[[インデクサ]](C++の<code>operator[]</code>に相当)をサポートする。インデクサの定義構文はプロパティと似ているが、<code>this[]</code>という名前をもち、一つ以上の引数(インデックス)を持つ点が異なる。インデクサを定義する際、インデックスには任意の型を使用することができる。 |
C#は[[インデクサ]](C++の<code>operator[]</code>に相当)をサポートする。インデクサの定義構文はプロパティと似ているが、<code>this[]</code>という名前をもち、一つ以上の引数(インデックス)を持つ点が異なる。インデクサを定義する際、インデックスには任意の型を使用することができる。 |
||
< |
<syntaxhighlight lang="csharp"> |
||
var myList = new System.Collections.Generic.List<int>(new int[10]); |
var myList = new System.Collections.Generic.List<int>(new int[10]); |
||
myList[4] = -100; |
myList[4] = -100; |
||
246行目: | 246行目: | ||
gravityTable["Earth"] = 1.0; |
gravityTable["Earth"] = 1.0; |
||
double gravityOnVenus = gravityTable["Venus"]; |
double gravityOnVenus = gravityTable["Venus"]; |
||
</ |
</syntaxhighlight><!-- |
||
C# 3.0以降は以下の初期化子リスト構文もサポートするが、ここではインデクサに関する比較がメインなので、あえて使用していない。 |
C# 3.0以降は以下の初期化子リスト構文もサポートするが、ここではインデクサに関する比較がメインなので、あえて使用していない。 |
||
var gravityTable = new System.Collections.Generic.Dictionary<string, double> { {"Mercury", 0.377}, {"Venus", 0.9}, {"Earth", 1.0} }; |
var gravityTable = new System.Collections.Generic.Dictionary<string, double> { {"Mercury", 0.377}, {"Venus", 0.9}, {"Earth", 1.0} }; |
||
253行目: | 253行目: | ||
C#は(論理的一貫性を保つための制限はあるものの)ユーザー定義の演算子オーバーロードをサポートしており、注意深く使用すれば簡潔で可読性の高いコードを記述することができる。 |
C#は(論理的一貫性を保つための制限はあるものの)ユーザー定義の演算子オーバーロードをサポートしており、注意深く使用すれば簡潔で可読性の高いコードを記述することができる。 |
||
< |
<syntaxhighlight lang="csharp"> |
||
var b1 = new System.Numerics.BigInteger(long.MaxValue); |
var b1 = new System.Numerics.BigInteger(long.MaxValue); |
||
var b2 = new System.Numerics.BigInteger(1); |
var b2 = new System.Numerics.BigInteger(1); |
||
var b3 = b1 + b2; |
var b3 = b1 + b2; |
||
</syntaxhighlight> |
|||
</source> |
|||
また、C#では明示的メンバ実装 (Explicit Member Implementation) が可能である。これによって、インターフェイスメソッドの実装とクラス自身のメソッドの実装とを分離することができ、また同じ[[シグネチャ]](名前および引数の数と型の順序)を持つメソッドが異なるインターフェイスにそれぞれ存在した場合に、インターフェイス名を指定して区別することでそれらの実装を別々に行うことができる。 |
また、C#では明示的メンバ実装 (Explicit Member Implementation) が可能である。これによって、インターフェイスメソッドの実装とクラス自身のメソッドの実装とを分離することができ、また同じ[[シグネチャ]](名前および引数の数と型の順序)を持つメソッドが異なるインターフェイスにそれぞれ存在した場合に、インターフェイス名を指定して区別することでそれらの実装を別々に行うことができる。 |
||
323行目: | 323行目: | ||
=== 統合言語クエリ === |
=== 統合言語クエリ === |
||
C#には、[[統合言語クエリ]]の為の拡張されたキーワード群が存在し、ソースコード上でSQL構文に似たクエリを直接記述することができる。このキーワード群は構文糖として機能し、決められた拡張メソッド呼び出しに展開される。 |
C#には、[[統合言語クエリ]]の為の拡張されたキーワード群が存在し、ソースコード上でSQL構文に似たクエリを直接記述することができる。このキーワード群は構文糖として機能し、決められた拡張メソッド呼び出しに展開される。 |
||
< |
<syntaxhighlight lang="csharp"> |
||
// 数列から偶数を抜き出して表示する |
// 数列から偶数を抜き出して表示する |
||
var inputs = new[] { 1, 5, 2, 3, 4, 7, 11, 10, 6 }; |
var inputs = new[] { 1, 5, 2, 3, 4, 7, 11, 10, 6 }; |
||
342行目: | 342行目: | ||
Console.WriteLine(resultValue); |
Console.WriteLine(resultValue); |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
統合言語クエリの拡張メソッド群は、コレクションに対するさまざまな集合演算が実装されており、このような演算をロジックで記述することなく、簡単に操作することができる。 |
統合言語クエリの拡張メソッド群は、コレクションに対するさまざまな集合演算が実装されており、このような演算をロジックで記述することなく、簡単に操作することができる。 |
||
また、ラムダ式から式ツリー(Expressionクラス)のインスタンスを生成することが出来るため、この機能を使用してデータベースへのクエリ発行・収集を、タイプセーフ性を失うことなく直接的に行うことができる(LINQ to SQL、LINQ to Entitiesなど)。 |
また、ラムダ式から式ツリー(Expressionクラス)のインスタンスを生成することが出来るため、この機能を使用してデータベースへのクエリ発行・収集を、タイプセーフ性を失うことなく直接的に行うことができる(LINQ to SQL、LINQ to Entitiesなど)。 |
2020年7月5日 (日) 22:51時点における版
C#とJavaの比較(シーシャープとジャバのひかく)の記事では、プログラミング言語C#とJavaの比較について説明する。
言語
オブジェクトの扱い
いずれの言語もオブジェクト指向言語であり、その文法はC++に類似しているが、C++との互換性はない。メモリ再利用の手段として、従来の手動で解放する方法ではなくガベージコレクションを使用する。また、スレッド同期の手段を言語構文に組み込んでいる。
いずれの言語も強い参照と弱い参照の両方をもつ。Javaでは参照がガベージコレクタによって回収された時に通知を受けるリスナーを登録することができる。これはWeakHashMap
の性能を考慮したものである。C#にはこれに相当する機能はなく、ファイナライザ(Javaにも存在する)を使用する方法しかない。その一方、C#は指定したオブジェクトのファイナライザ呼び出しをプログラマが抑止することができる。「世代」の概念をもつガベージコレクション(Javaと.NETいずれにも当てはまる)においてファイナライザの呼び出しは性能に大きな影響を与えるため、またファイナライザはプログラマがオブジェクトを破棄しなかった場合のフェイルセーフであるため、これは非常に有効である。ファイナライザをもつオブジェクトは通常余分な世代が与えられ、回収されるまでに長くかかる。
C#は、一部の言語設計者から危険であると指摘されるポインタを使用した演算が制限つきながら利用できる。C#はポインタを使用するコードブロックあるいはメソッドをunsafe
キーワードで修飾することでこの懸念に対応している。これにより、このコードを利用する者はそれが他の部分に比べて危険であるということを知ることができる。また、このようなコードをコンパイルする際にはコンパイラに対して/unsafeスイッチを指定する必要がある。一般に、unsafeコードが使われるのはアンマネージなAPIやシステムコールとの相互運用が必要な時、あるいは性能の向上が必要な時のみである。
型システムとデータ型
Javaには大別して参照型(クラス型)とプリミティブ型(基本型)が存在する。参照型はjava.lang.Object
クラスから派生するが、プリミティブ型はスーパークラスを持たない。
一方、C#には大別して参照型、値型、ポインタ型が存在する[1]。このうちポインタ型を除き、あらゆる型はすべてSystem.Object
クラスから派生する。クラス (class
) は参照型であり、数値型や論理型を含む構造体 (struct
) および列挙型 (enum
) は抽象クラスSystem.ValueType
から暗黙的に派生する値型である[2]。
Javaの組み込み型 (built-in type) はプリミティブ型と呼ばれる。C#は組み込み型をJavaよりも多く持ち、すべての組み込み型はSystem
名前空間に存在する型へのエイリアスである[3][4]。組み込み型には以下のようなものがある。
- 整数型: Javaには符号付き整数のみをサポートし、符号なし整数が存在しない。C#では符号付き整数と符号なし整数の両方をサポートする。
- 浮動小数点数型: いずれの言語もIEEE 754準拠の32bit単精度浮動小数点数と64bit倍精度浮動小数点数を持つ。
- C#ではさらに、10の累乗の形で指数部を表す浮動小数点数である
decimal
型をサポートする。decimal
型は値型かつ組み込み型であるが、CLRのプリミティブではない。
- 文字型: いずれの言語もUTF-16ベースの文字型を持つ。
- ブーリアン型: いずれの言語も
true
/false
のいずれかを表すブーリアン型を持つ。 - オブジェクト型: C#では組み込み型として
object
型を持つ。Javaのオブジェクト型java.lang.Object
は組み込み型ではない。 - 文字列型: C#では組み込み型として
string
型を持つ。Javaの文字列型java.lang.String
は組み込み型ではない。
- 文字列はいずれの言語においても不変 (immutable) なオブジェクトとして扱われるが、特殊な構築方法として文字列リテラルを利用することができる。C#ではエスケープ文字を処理しないような逐語的文字列リテラル(verbatim文字列: ヒアドキュメントを参照)をサポートする。
C#言語自体にはプリミティブ型という用語は存在しないが、.NET Frameworkの共通言語ランタイム (CLR) では、System.Type.IsPrimitive
プロパティによって、型がCLRプリミティブ型であるかどうかを判定できる[5]。.NET言語組み込みの値型は必ずしもCLRプリミティブ型ではないが、CLRプリミティブ型はすべて値型である。
いずれの言語もプリミティブ型(もしくは値型)と参照型との間で変換するためにボックス化 (boxing) とボックス化解除 (unboxing) が可能である。これによってプリミティブ型(もしくは値型)は参照型のサブセットとみなすことができる。値型は仮想メソッドテーブルを持たず、したがってそのままでは多態性を利用できないが、C#においては、ボックス化によって値型で多態性を利用することが可能である(例えばobject
型のToString()
メソッドをオーバーライドすることができる)。C#では数値リテラルもオブジェクトであり、たとえば42.ToString()
のようにint
型のリテラルからメソッドを呼び出すことも可能である。Javaではこのような用途のためにプリミティブ型をラップするクラスが別に定義される。すなわち、42.toString()
のようなインスタンスメソッド呼び出しでなくInteger.toString(42)
のような静的メソッド呼び出しが必要になる。もう一つの相違点として、Javaではジェネリクスにおいてこのような型を多用するため、暗黙的なボックス化解除が可能になっている(C#ではボックス化せずにジェネリクスを利用できる)。このような変換はnullポインタ例外を発生する可能性があるが、Javaではそれがコード上で明白ではない。
C#では、struct
キーワードによって構造体を定義することができる。構造体にはフィールドやメソッド、プロパティなどの任意のメンバーを定義できる。引数を持たないデフォルトコンストラクタをプログラマが定義することはできないが、一つ以上の引数をもつパラメータ化されたコンストラクタを定義することはできる。デフォルトコンストラクタはすべてのメンバーを各々の既定値(通例ゼロ相当の値)で初期化する。また構造体を定義する際、メンバーのメモリレイアウトを属性によって細かく制御することができるため、P/Invokeなどによるネイティブコードとの相互運用にも便利である。また、構造体は継承元となる基底型を指定することはできず、派生型を定義することもできない。ただし任意のインターフェイスの実装は可能である。Javaには構造体が存在せず、ユーザー定義の値型を作成することはできない。
C#の列挙型は抽象クラスSystem.Enum
から暗黙的に派生する値型の一種であり、組み込みの整数型をベースとしている。ベースとなる整数型のどの値も列挙型の値として有効になる(明示的なキャストは必要であるが)。このため、ビットフラグにおいてビットごとのOR演算で列挙型の値を組み合わせることが可能である。ただし構造体と違って任意のインターフェイスを実装することはできない。一方、Javaの列挙型は抽象クラスjava.lang.Enum
から暗黙的に派生する参照型である。Javaの列挙型として有効な値は定義においてリストされたものだけである。列挙型の値を組み合わせるためには列挙セットクラスを使用する必要がある。Javaの列挙型では、値によって異なるメソッドの実装が可能である。JavaとC#はいずれも列挙型を文字列に変換することができるが、Javaにおいてはこの変換をカスタマイズすることができる。また、Javaの列挙型は任意のインターフェイスを実装することができる。
プリミティブ型(あるいは値型)は参照型(クラス)とは異なり、インスタンスはヒープ領域ではなくスタックに置かれる。また、(フィールドとして、あるいはボックス化された状態で)クラスの一部になることも、配列の要素になることも可能である。クラスのインスタンスはメモリ上で間接的に参照される必要があるが、プリミティブ型(あるいは値型)はその必要がない。プログラマの視点からは、C#の値型は軽量なクラスとみなせる。しかし、プリミティブ型(あるいは値型)には前述のように多数の制限がある。値型は通常nullの値をとることができないが、.NET Framework 2.0でnull許容型 (System.Nullable
) が導入され、C#でも擬似的にnull値を取り得る値型が利用可能となった。
配列
C#およびJavaの配列は参照型であり、第一級オブジェクトである。C#の配列は共通の基本クラスSystem.Array
から派生するが、Javaの配列はjava.lang.Object
から派生する。代わりに、配列のユーティリティクラスとしてjava.util.Arrays
が用意されている。
いずれの言語も配列は共変性 (covariance) を持つ。
System.Console.WriteLine(typeof(int[]).BaseType); // System.Array
object[] objs = new string[3]; // 代入可能。
objs[0] = "test";
objs[0] = 10; // System.ArrayTypeMismatchException
System.out.println(int[].class.getSuperclass()); // java.lang.Object
Object[] objs = new String[3]; // 代入可能。
objs[0] = "test";
objs[0] = 10; // java.lang.ArrayStoreException
なお、Java 1.5で拡張for文がサポートされた。これはC#のforeach
文に相当する。配列を始めとするコレクション型を、イテレータにより走査することができる。
C#は真の多次元配列(矩形配列)をサポートするが、Javaはサポートしない。C#およびJavaはともに「配列の配列」をサポートする(C#ではジャグ配列と呼ばれる)。
内部クラス
いずれの言語もネストされた型(入れ子にされた型:クラスや構造体のブロックの中で定義されたクラス・構造体・列挙型・インターフェイス)をサポートする。Javaではインターフェイス内部にクラスや列挙型を定義することもできる。C#ではインターフェイス内部にクラス・構造体・列挙型・インターフェイスを定義することはできない。
Javaでは、ネストされたクラス (nested class) は既定で内部クラス (inner class) となる。内部クラスは外側のクラスのインスタンスを暗黙的にキャプチャすることで、静的メンバ、非静的メンバいずれにもアクセスすることができる。ネストされたクラスがstatic
修飾されていた場合は静的メンバのみにアクセスできる。メソッドの内部にクラスを定義することもでき、これはローカルクラス (local class) と呼ばれる。ローカルクラスでは、外側のローカル変数には読み取りアクセスのみできる。また、型の名前を持たないローカルクラス(匿名クラス: anonymous class)を定義し、同時にインスタンス生成をすることもできる。
C#では、ネストされたクラス/構造体から外側のクラス/構造体の非静的メンバにアクセスするためには外側のクラス/構造体のインスタンスへの明示的な参照が必要になる。Javaの内部クラスやローカルクラスに相当する機能は存在しない。代わりに、C# 2.0以降では匿名メソッド (anonymous method) が、C# 3.0以降ではラムダ式が、そしてC# 7.0以降ではローカル関数がサポートされ、外側の変数をキャプチャするクロージャとして利用できる。なお、C# 3.0では限定的なローカルクラスとして、匿名型(読み取り専用プロパティのみを持つ、匿名のクラス型)がサポートされる。
ジェネリクス
Javaではジェネリクスは型消去 (type erasure) によって実装されている。これによってジェネリック型についての情報は実行時には失われ、リフレクションを通してのみ取得できるようになる。.NET 2.0では、ジェネリック型についての情報は完全に保存される。Javaはプリミティブ型に対するジェネリック型は定義できないが、C#では参照型・値型(プリミティブ型を含む)いずれに対してもジェネリック型を定義できる。Javaはその代わりにボックス化した型を使用することができる(List<int>
の代わりにList<Integer>
など)が、全ての値をヒープに確保し直す必要があるため、パフォーマンスコストが高い。JavaとC#はいずれも、参照型に特殊化されたジェネリック型は、型によらず共通のコードが実行される。しかし、C#において値型に特殊化された場合、CLRは型に最適化されたコードを動的に生成する。.NETにおいては、ジェネリック型に対する型安全性はコンパイル時にチェックされ、CLRにロードされる時に強制される。Javaにおいてはコンパイル時に部分的にチェックされるのみであり、Java VMは実行時にジェネリック型に関する情報を持たないため、キャスト操作を行う必要がある。
ジェネリクスの型制約
C#、Java共に、ジェネリクスの型制約を持つ。C#では基底型指定以外に、class
(参照型)、struct
(値型)、new()
(デフォルトコンストラクタ有)の各制約を指定できる。
// C#
class ConstraintClass { }
class SomeGenericsClass<T> where T : ConstraintClass { }
class OtherGenericsClass<T> where T : new() { }
// Java
class ConstraintClass { }
class SomeGenericsClass<T extends ConstraintClass> { }
ジェネリクスの共変性と反変性
C#、Java共に、ジェネリクスの共変性と反変性 (covariance and contravariance) を持つ。ただしC#における共変性・反変性のサポートはバージョン4.0以降である。
C#では、型定義側で共変性out
あるいは反変性in
を指定し、利用側では変性の指定を行わない。なお、C#の値型は不変 (invariant) であり、共変性・反変性は適用されない。
// C#
// System.Func<T, TResult> と同様のデリゲートを定義。
// 型定義側で変性の指定を行う。
public delegate TOut MyFunc<in TIn, out TOut>(TIn arg);
public class GenericsTest {
public static void Main() {
// 利用側では変性の指定を行わない。
MyFunc<object, string> func1 = x => "<" + x + ">";
MyFunc<string, object> func2 = func1;
object ret = func2("test");
System.Console.WriteLine(ret);
System.Console.WriteLine(ret.GetType()); // System.String
}
}
Javaでは、型定義側では変性の指定を行わず、利用側で共変性extends
あるいは反変性super
を指定する。
Javaではさらに、上限と下限のいずれも指定しないワイルドカード?
による型引数の指定が可能である。以下にJava 8の例を示す。
// Java
// java.util.function.Function<T, R> と同様のインターフェイスを定義。
// 型定義側では変性の指定を行わない。
@FunctionalInterface
interface MyFunc<TIn, TOut> {
TOut apply(TIn arg);
}
public class GenericsTest {
public static void main(String[] args) {
// 利用側で変性の指定を行う。
MyFunc<? super Object, ? extends String> func1 = x -> "<" + x + ">";
MyFunc<? super String, ? extends Object> func2 = func1;
Object ret = func2.apply("test");
System.out.println(ret);
System.out.println(ret.getClass().getName()); // java.lang.String
// 変性の上下限いずれも指定しないこともできる。
java.util.List<?> list;
}
}
型推論
C# 3.0でコンテキストキーワードvar
による限定された型推論が導入された。ローカル変数の宣言時に、型を右辺から推論できる。メソッド引数やフィールドには使えない。また、ラムダ式の戻り値および仮引数は型推論により決定される。ラムダ式の仮引数は型を省略することで型推論されるが、型推論が困難な場合には明示的に型を指定する。
Java 7で導入されたダイヤモンド演算子<>
は宣言文の右辺ジェネリクスの型を省略できる程度のものでしかなかったが、Java 10ではC#同様に予約型名var
によるローカル変数の型推論が導入された。Java 8で導入されたラムダ式では、仮引数の型を省略することで型推論されるが、さらにJava 11ではラムダ式の仮引数の型にvar
を使用して型推論できるようになった。
表記法と特殊な仕様
Java 1.5で、あるクラスの静的メソッド・静的フィールドを短い名前で使用するためにstatic import
構文が導入された。これにより、例えばClassA
クラスのfoo()
メソッドを静的インポートして、無関係のClassB
クラスにてClassA.foo()
のように型名で修飾することなく、foo()
を直接使用できる。C# 6.0でも同様の機能としてstatic using
ディレクティブがサポートされた。
C#は静的クラス(Javaの静的内部クラスとは異なる)の構文をもち、これによってクラスは静的メソッドのみを持つことができるようになる。C# 3.0から、型に静的にメソッドを追加するための拡張メソッドが導入されている(Foo
型のインスタンスfoo
についての処理を行うBar()
拡張メソッドを定義することで、あたかもFoo
にインスタンスメソッドが追加されたかのように、foo.Bar()
と記述できる)。この機能は、しばしばオブジェクト指向におけるカプセル化の概念を破壊するものとして誤解される[要出典]が、ターゲットとなるクラスのプライベートメンバにアクセスすることはできないため、そのような事は無い。
キーワード
キーワード | 仕様・使用例 |
---|---|
get, set | C#では、Javaにおけるアクセサメソッドへの代替としてプロパティを言語構文としてサポートする。 |
out, ref | C#ではメソッド引数をout またはref で修飾することで、引数への出力あるいは参照をサポートする。これによって複数の戻り値を得たり、参照渡ししたりといったことが可能になる。
C# 7.0ではさらにメソッドの戻り値を |
switch | C#ではswitch文で整数型、char 型、bool 型、列挙型、それらの型のNullable 、およびstring 型を使うことができる。C#ではフォールスルーを許可しない(case ラベルが連続している場合のみ許可される)。C# 7.0ではさらに「型パターン」により型ベースの分岐が可能である。
Javaでは |
strictfp | Javaでは、異なるプラットフォーム間で実数演算結果が同じになるよう保証するstrictfpキーワードを利用できる。 |
checked, unchecked | C#では、checked ブロック内(あるいは式単体)では実行時に数値オーバーフローがチェックされる。
|
using, try | C#では、using ステートメントによって、作成されたオブジェクトがブロックを抜ける際に確実に破棄されるよう強制することができる。
// "test.txt"というファイルを作成し、文字列を書き込み、(例え例外が発生しても)確実に閉じる。
using (StreamWriter file = new StreamWriter("test.txt"))
{
file.Write("test");
}
Java 7では、try-with-resources構文で同様のリソース解放に対応している。 |
goto | C#ではgoto文がサポートされる。これは便利な場面もあるが、通常はより構造化されたフロー制御方法が推奨される。C#ではswitch ステートメントにおいてgoto キーワードを使うことで、異なるcase ラベルに移ることができる。
switch (color)
{
case Color.Blue: Console.WriteLine("Color is blue"); break;
case Color.DarkBlue: Console.WriteLine("Color is dark"); goto case Color.Blue;
// ...
}
Javaではgoto文はサポートされない。ただし |
yield | C#ではコンテキストキーワードyield が用意されており、yield return 文とyield break 文を使用してイテレータ(コルーチン)を記述できる。これらの文を含むメソッド(イテレータ)の戻り値はIEnumerableインターフェイス、またはIEnumeratorインターフェイスでなければならない。
また、イテレータは、内部的に状態を持つステートマシンとしてコンパイルされ、値を部分的に返却する列挙子を提供するように振る舞う。 // 指定された個数の乱数を生成する
public IEnumerable<int> RandomValueGenerator(int count)
{
var r = new Random();
for (var index = 0; index < count; index++)
{
yield return r.Next();
}
}
|
イベント処理
Javaでは、Observer パターンの記述を容易にするために匿名クラスという糖衣構文が用意されている。匿名クラス構文により、クラス本体の定義とインスタンスの生成を同時に行なうことができ、また定義と生成がひとつの式として扱えるため、インターフェイス実装クラスのインスタンスを一度限りしか生成しない場面、例えば特定のインターフェイス実装(あるいは特定のスーパークラスのメソッドのオーバーライド)を要求するイベントハンドラーの設定時などによく用いられる。Java 8ではラムダ式がサポートされ、匿名クラスの代わりに使うこともできる。Java 7まではC#のデリゲートに相当するものは存在しなかったが、Java 8では類似機能としてメソッド参照がサポートされるようになった。
C#ではデリゲート (delegate
) 型をはじめ、イベント処理をサポートするための機能が広範囲に渡って言語レベルでサポートされている。デリゲート型はメソッドへの型安全な参照であり、複数のデリゲートを結合してマルチキャスティングすることもできる。デリゲートを結合/分離するための+
, -
演算子、イベントハンドラーをカプセル化するためのイベント構文 (event
, add
/remove
) や、イベントハンドラーを登録/削除するための+=
, -=
演算子が用意されている。デリゲートは共変性と反変性 (covariance and contravariance) をサポートし、完全なクロージャとしての性質をもつ匿名関数(匿名メソッドおよびラムダ式)を作成することができる。
非同期処理
JavaおよびC#はともに標準ライブラリでスレッドをサポートしている。OSのスレッドに対する薄い抽象化を提供するクラスとして、Javaにはjava.lang.Thread
、C# (.NET) にはSystem.Threading.Thread
が用意されている。そのほか、セマフォなどの同期オブジェクトやアトミック演算なども標準ライブラリでサポートされている。
言語組み込みの同期構文として、Javaにはsyncronized
ブロックおよびsyncronized
メソッドが用意されている。C#にはlock
ステートメントが用意されている。
C# 5.0以降はasync
/await
コンテキストキーワードによる非同期メソッド構文がサポートされた。async/awaitはイテレータおよび.NET 4以降で追加されたタスク並列ライブラリ (Task Parallel Library; TPL) を実行基盤とする糖衣構文であり、非同期タスクの完了待機と実行結果の取得をあたかも同期メソッドのように記述することができる。
数値処理
数学・科学計算・金融分野のアプリケーション開発に十分対応するための言語仕様が、それぞれに存在する。Javaでは厳密な浮動小数点計算を強制するためにstrictfp
キーワードが存在する。これによってあらゆるプラットフォームで必ず同じ値が結果として得られることを保証することができる。C#にはこれに相当する機能はないが、厳密な浮動小数点計算のためにdecimal
型が存在する。これによって二進浮動小数点表現(float
、double
)に存在する問題が解決される(二進表現の浮動小数点数は十進数を正確に表現することができないため、丸め誤差が生じてしまう)。金融分野のアプリケーションソフトウェアでは厳密な十進表現は必須である。Javaでは、このような用途のためにjava.math.BigDecimal
が導入されている。また、Java 1.1では巨大な整数を扱うためのクラス型としてjava.math.BigInteger
が導入されている。BigDecimal
とBigInteger
は最大で約21億桁程度までの数を任意精度で表現できる。C#においては、.NET Framework 4よりSystem.Numerics.BigInteger
型が導入されている。
Javaでは、BigDecimal
や複素数型といったライブラリ定義の型をプリミティブ型と同じレベルで使用することは不可能である。また、Javaではユーザー定義型はすべて参照型扱いとなり、メモリブロックの実体はヒープに確保される。一方、C#は次のような機能をサポートする。
- 演算子多重定義やインデクサ。
- 暗黙的または明示的な型変換。C++のキャスト演算子オーバーロードに似た、ユーザー定義の型変換を定義できる。これにより、例えばプリミティブ数値型同士の変換と類似の機能を提供できる。
- 値型と値型に対するジェネリック型。値型はスタックに確保される。これは実行時の性能面に影響を及ぼす。
これらに加え、C#では数値処理アプリケーションのために、コード中の特定領域の数値オーバーフローの実行時チェックの有効・無効を制御することができるchecked
/unchecked
キーワードを使用できる。
演算子多重定義
Javaは言語仕様をシンプルに保つため、また乱用による難読化を防ぐため、ユーザー定義の演算子オーバーロードをサポートしていない。一方でコードの書きやすさおよび直感性は犠牲になっている。
var myList = new java.util.ArrayList<Integer>(java.util.Collections.nCopies(10, 0));
myList.set(4, -100);
var gravityTable = new java.util.HashMap<String, Double>();
gravityTable.put("Mercury", 0.377);
gravityTable.put("Venus", 0.9);
gravityTable.put("Earth", 1.0);
double gravityOnVenus = gravityTable.get("Venus");
var b1 = new java.math.BigInteger(Long.toString(Long.MAX_VALUE));
var b2 = new java.math.BigInteger(Integer.toString(1));
var b3 = b1.add(b2);
C#は表記法の多くの点でJavaよりも多機能である。演算子多重定義やユーザー定義キャストなど、それらの多くはC++プログラマによって既に親しまれているものである。
C#はインデクサ(C++のoperator[]
に相当)をサポートする。インデクサの定義構文はプロパティと似ているが、this[]
という名前をもち、一つ以上の引数(インデックス)を持つ点が異なる。インデクサを定義する際、インデックスには任意の型を使用することができる。
var myList = new System.Collections.Generic.List<int>(new int[10]);
myList[4] = -100;
var gravityTable = new System.Collections.Generic.Dictionary<string, double>();
gravityTable["Mercury"] = 0.377;
gravityTable["Venus"] = 0.9;
gravityTable["Earth"] = 1.0;
double gravityOnVenus = gravityTable["Venus"];
C#は(論理的一貫性を保つための制限はあるものの)ユーザー定義の演算子オーバーロードをサポートしており、注意深く使用すれば簡潔で可読性の高いコードを記述することができる。
var b1 = new System.Numerics.BigInteger(long.MaxValue);
var b2 = new System.Numerics.BigInteger(1);
var b3 = b1 + b2;
また、C#では明示的メンバ実装 (Explicit Member Implementation) が可能である。これによって、インターフェイスメソッドの実装とクラス自身のメソッドの実装とを分離することができ、また同じシグネチャ(名前および引数の数と型の順序)を持つメソッドが異なるインターフェイスにそれぞれ存在した場合に、インターフェイス名を指定して区別することでそれらの実装を別々に行うことができる。
メソッド
C#のメソッドはデフォルトで非仮想(非virtual)であり、仮想メソッドにする必要がある場合は明示的にvirtual
と宣言しなければならない。Javaのメソッドは常に仮想 (C#でvirtual指定された状態) であり、非仮想にすることはできないが、final
修飾子を指定することでオーバーライドを禁止することはできる。
Javaでは、メソッドを非仮想的にする方法はない。これは、派生クラスが同名の無関係なメソッドを再定義することが不可能であることを意味する。これは基底クラスが別のプログラマによって書かれており、バージョン更新の際に、派生クラスに既に存在していたものと同じシグネチャのメソッドが追加されてしまった場合に問題になる。Javaでは、この場合どちらのプログラマの意図とも異なり、派生クラスのメソッドは暗黙的に基底クラスのオーバーライドになってしまう(基底クラスでfinal宣言されていた場合はコンパイルエラーになってしまう)。
これらのバージョン更新の問題を部分的に解決するため、Java SE 5.0では@Override
アノテーションが導入された。これにより、基底クラスが同一シグネチャのメソッドを持ち、それが派生クラスで正しくオーバーライドされていることを保証することはできる。しかし後方互換性維持のため、この指定は必須ではない。従ってIDEやツールなしに上記のように思いがけずオーバーライドしてしまうような問題を防ぐことはできない。
C#では、派生クラスで仮想メソッドをオーバーライドする際には、明示的にその旨を宣言する必要がある。メソッドが基底クラスのオーバーライドである場合、override
修飾子が指定されていなければならない。また、オーバーライドではなく同名の無関係なメソッドを派生クラスで再定義することができるが、コンパイラが警告を発する。コンパイラ警告を抑制したい場合、new
修飾子を指定しなければならない。
メソッド種別 | C# | Java |
---|---|---|
非仮想 | (指定なし) | N/A |
仮想 | virtual |
(指定なし) |
抽象 | abstract |
abstract
|
オーバーライド | override |
@Override
|
継承先でのオーバーライド禁止 | (指定なし:非仮想) | final
|
オーバーライド かつ 継承先でのオーバーライド禁止 | sealed override |
@Override final
|
隠蔽 | new |
N/A |
プリプロセッサ
JavaはC/C++でしばしば問題を引き起こしていたプリプロセッサを採用しなかった。一方でC#は限定的にプリプロセッサディレクティブを使用可能である。
条件付きコンパイル
Javaではプリプロセッサがサポートされないため、コンパイル時の分岐は不可能となっている。
一方、C#ではプリプロセッサディレクティブを用いた条件付きコンパイル (条件コンパイル、英: conditional compilation) が実装されている。また、指定されたコンパイル定数が定義されている時のみ呼び出されるようConditional
属性をメソッドに指定することができる。この方法によって、DEBUG
定数が定義されている時のみ評価される表明(アサート)機能 (System.Diagnostics.Debug.Assert()
メソッド) が提供されている。Java 1.4からは実行時に有効・無効を切り替えられるassert
文が言語仕様として導入されている。
名前空間とソースファイル
C#の名前空間はC++のそれと類似している。Javaのパッケージとは異なり、名前空間はソースファイルの物理的位置とは無関係である。Javaではパッケージ構造とソースファイルの位置が必ずしも一致している必要はないものの、デフォルトではそのような振る舞いをする。
Javaではアクセスレベルがpublic
のクラス名はソースファイル名 (*.java) と一致していなければならない。つまり1つのソースファイルにpublic
クラスは1つだけしか定義できない。デフォルトのアクセスレベルのクラスであれば、1つのソースファイルに複数のクラスをまとめて記述することができる。
C#ではそのような制約はなく、クラス名と無関係の1つのソースファイル (*.cs) に任意のクラスを複数記述することができる。C# 2.0からはpartial
キーワードによってクラスの定義を複数のファイルに分けて記述することが可能になった。
例外処理
Javaは非チェック例外 (unchecked exception) に加えてチェック済み例外 (checked exception) をサポートする。C#では非チェック例外のみである。チェック済み例外は、プログラマがメソッドから発生し得るものを全て宣言し、捕捉する必要がある。
全てのエラーが処理されることを保証できるため、チェック済み例外は非常に便利だとする者もいる。一方、C#の設計者であるアンダース・ヘルスバーグのように、Javaのチェック済み例外はある程度実験的な仕様であり、小さなプログラムでの例を除いては実装する価値を見出せなかった、とする者もいる[7][8]。一つの批判として、チェック済み例外はプログラマが空のcatchブロックを記述するのを促進し、catch (
のような危険なコードを増やす結果になってしまったというものがある。また別の批判として、メソッドの実装に変更を加えた結果新しいチェック済み例外が発生するようになる可能性があり、これによって契約が破壊されてしまう、というものがある。これは限られた例外のみが宣言されたインタフェースを実装するメソッドや、メソッドの内部実装が変更された場合に起こり得る。中には、このような予期しない例外が発生することを見越し、あらゆる型の例外が発生し得る、と宣言 (Exception
e) {}throws Exception
) するプログラマもいる。これはチェック済み例外の利点を無にしている。しかしながら、いくつかの場面では例外連鎖 (exception chaining)、すなわち捕捉した例外を別の例外でラップして投げ直す、という手法が適用できる。例えば、ファイルにアクセスするコードがデータベースにアクセスするよう変更された場合、呼び出し側は内部で何が行われているかを知る必要がないため、SQLException
が捕捉された場合でもIOException
として投げ直すことが可能である。
try-finally
ステートメントにおいても両者は異なる。finally
は例えtry
ブロック内でthrow
やreturn
が実行された場合でも必ず実行される。これは、try
内とfinally
内で異なる値がreturn
された場合に予期しない振る舞いを生じることがある。C#ではfinally
ブロック内ではreturn
やbreak
といった文の実行を禁止している。
低レベルコード
C/C++などで書かれたネイティブコード資産の再利用や、オペレーティングシステムあるいはハードウェアへのローレベルなアクセスを可能とするため、JavaおよびC#はともにネイティブ相互運用のための機能を提供している。
Java Native Interface (JNI) ではJavaコード内で非Javaコードをnativeメソッドとして呼び出すことができる。しかしながら、JNIは呼び出されるコードのインターフェイス(シグネチャ)に制限がある。このため、Javaと既存のネイティブコード資産との間に余分なレイヤーが必要になることがよくある。このレイヤーはJavaではない言語で書かれる必要があり、CやC++がよく用いられる。JNIを利用することで、逆にC/C++側からJavaのクラスライブラリにアクセスすることも可能である。
.NETのプラットフォーム呼び出し(Platform Invoke、P/Invoke)はC#からアンマネージコードの呼び出しを可能にする。プログラマはメタデータを通して、メソッドの引数や戻り値がどのように橋渡し(マーシャリング)されるかを完全に制御することができる。このため、既存コードのインターフェイスがC言語形式関数でありさえすれば、余分なレイヤーは必要にならない。P/Invokeは(Win32やPOSIXなどの)手続き型APIにはほぼ完全にアクセスすることができるが、C++クラスライブラリへの直接的なアクセスはきわめて困難である。そのほか、C++/CLI言語を介することで、C#とC/C++間の相互運用を行なうことも可能である。またCOM相互運用により、コード資産を相互に利用することも可能である。
C#は通常の型チェックなどのCLRの安全のための機能を無効にし、ポインタ変数を利用することができる。この時、プログラマはコードをunsafe
キーワードでマークする必要がある。JNI、P/Invoke、unsafe
コードはどれも同様に「危険な」機能であり、セキュリティホールやアプリケーションの不安定性につながる恐れがある。unsafe
コードがP/InvokeやJNIに対して優れている点は、アンマネージコードを呼び出すことなくC#の機能内でタスクを完結できる点である。unsafe
コードを含むアセンブリはコンパイル時にそのように明示的に指定する必要がある。これにより、実行環境は危険な可能性があるコードであるということを実行する前に知ることができる。
統合言語クエリ
C#には、統合言語クエリの為の拡張されたキーワード群が存在し、ソースコード上でSQL構文に似たクエリを直接記述することができる。このキーワード群は構文糖として機能し、決められた拡張メソッド呼び出しに展開される。
// 数列から偶数を抜き出して表示する
var inputs = new[] { 1, 5, 2, 3, 4, 7, 11, 10, 6 };
#if true
// LINQクエリ構文
var results =
from inputValue in inputs
where (inputValue % 2) == 0
select inputValue;
#else
// LINQメソッド構文
var results = inputs.
Where(inputValue => (inputValue % 2) == 0).
Select(inputValue => inputValue);
#endif
foreach (var resultValue in results)
{
Console.WriteLine(resultValue);
}
統合言語クエリの拡張メソッド群は、コレクションに対するさまざまな集合演算が実装されており、このような演算をロジックで記述することなく、簡単に操作することができる。 また、ラムダ式から式ツリー(Expressionクラス)のインスタンスを生成することが出来るため、この機能を使用してデータベースへのクエリ発行・収集を、タイプセーフ性を失うことなく直接的に行うことができる(LINQ to SQL、LINQ to Entitiesなど)。 Javaでは、このようなクエリ構文を直接サポートしない。Java8ではC#のLINQメソッド構文に近い記述が可能となるStream APIとラムダ式がサポートされた。
実装
JVMとCLR
Javaはまったく異なる多くのオペレーティングシステム間で実行できる。またパーソナル・コンピュータに限らず、高度な計算処理や制御を必要とする家電製品や、Blu-ray Discのインタラクティブ技術にもBD-Jとして使用されている。このように数多くのJava仮想マシン (Java VM, JVM) 実装が存在する。
C#および.NETテクノロジーもやはりクロスプラットフォームである。.NET Frameworkはマイクロソフトによる.NETの実装であり、共通言語ランタイム (CLR) はマイクロソフトによる共通言語基盤 (CLI) の実装である。主なプラットフォームはWindowsだが、他のプラットフォームにも実装が存在する。有名なものにMonoがある。ただし、マイクロソフトによる実装と比較して未実装部分が多く、利用できるライブラリに大きく制限がある。マイクロソフトによるモバイル/組み込み環境向け実装としては.NET Compact Frameworkがある。
2017年現在、.NET Frameworkの他に.NET Core[9]やMono/Xamarinなどの実装が存在し、多くのオペレーティングシステム向けの開発が可能となっている。
標準
両言語の構文(文法)、プログラミングインタフェース、バイナリ形式(実行ファイル形式)、実行環境などは様々な機関によって管理されている。
C#はEcmaとISO、JISによって定義されている。標準化の対象は言語構文、基本クラスライブラリ、アセンブリ形式、実行環境(共通言語基盤: CLI)など多岐に渡る。下位層フレームワークの上に新しく実装された上位層ライブラリの多くはこの標準には含まれない(Windows Forms、ASP.NET、ADO.NETなど)。
現在のところ、Javaのどの部分も第三者の標準化団体によって標準化されていない。Javaの商標、ソースコードやその他の素材に関してはオラクル(旧サン・マイクロシステムズ)が無制限の独占的な権利を保持しているが、オラクル(サン)はJava Community Process[10] (JCP) と呼ばれるプロセスに参加し、当事者たちがJavaに関連する技術(言語、SDKからAPIに至るまで)に対する変更を専門家団体や諮問会議を通して提案することを許可している。JCP内の規定では、Javaに対する新しい仕様や変更はオラクル(サン)による承認が必要であるとされている。JCPは営利寄与者に対しては会費が必要としているが、非営利寄与者や個人は無料で参加できる。JavaのAPIセットにはいくつかのエディションがあり、標準エディションのJava SE、エンタープライズ向けエディションのJava EE、モバイル/組み込み環境向けエディションのJava MEが存在する。
姉妹言語
C#およびJavaには、それぞれの実行環境を用いて動作する姉妹言語が存在し、それぞれ.NET言語およびJVM言語と呼ばれている。姉妹言語は他の既存言語からの移行のしやすさや、記述能力および生産性の向上、あるいは新たなプログラミングパラダイムを導入するなどの目的で開発されたものであり、異なる言語間でユーザー定義のクラス型などを再利用する相互運用も可能である。
各々の代表的な姉妹言語を列挙する。
- .NET言語
- JVM言語
参照
- ^ 型 (C# リファレンス) | Microsoft Docs
- ^ 値型 (C# リファレンス) | Microsoft Docs
- ^ データ型 (C# と Java の比較) | Microsoft Docs
- ^ Built-in types table (C# Reference) | Microsoft Docs
- ^ Type.IsPrimitive Property (System) | Microsoft Docs
- ^ C# 7.0 の新機能 - C# ガイド | Microsoft Docs
- ^ The Trouble with Checked Exceptions
- ^ Why doesn't C# have exception specifications?
- ^ .NET Core とオープン ソース | Microsoft Docs
- ^ 「Javaはオラクルのもの?」、「いいえ、これからもJavaコミュニティのものです!」――Javaエバンジェリスト 寺田佳央氏が、Javaの現在、未来を語る