Javaに対する批判

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内検索

Javaは優れた技術だと評価する人々がいる一方で批判も少なくない。Javaはソフトウェアに関する複雑さを管理する問題に対して革新的な方法を提供するという目標のもとで開発された。多くの人々はJava技術はこの期待に対して満足できる答えを提供したと評価している。しかしJavaにも欠点が無いわけではないし、どのようなプログラミング作法にも適応しているというわけでもない。また、どのような環境や要件にも普遍的に適応しているというわけでもない。

クラスパス[編集]

Javaプログラムを実行する際、すべてのサードパーティーライブラリクラスパスに存在する必要がある。これは移植性の障壁となる。なぜならばクラスパスの文法はプラットフォームに依存するからである(Windowsベースのシステムはサブディレクトリを区切るためにバックスラッシュ "\" を使用し、パス区切りにセミコロン ";" を使用している[1]。だが一方、他の多くのプラットフォームではサブディレクトリ区切りにスラッシュ "/" を使用し、パス区切りにコロン ":" を使用している)[2]

各々の.jar.zipアーカイブはクラスパスにおいて明示的に名前がつけられる必要がある。この抜け道としてアスタリスク(*)で終わるクラスパスを指定することで、そのディレクトリにある.jarや.JARで終わるすべてのファイル名にマッチさせることができる。しかしながら、.zipや.classファイルのようなものはマッチしない[1][2]

解決策・代替策[編集]

これらのクラスパス問題は環境変数CLASSPATHを使用せず、サン・マイクロシステムズが推奨する-classpathオプションを使用することで解決する。開発時は、-classpathオプションはバッチファイルmakeApache Ant統合開発環境を使うことで便利に指定することができる。Javaアプリケーションを利用する実行エンドユーザに対しては、開発者がマニフェストファイルにクラスパスを記述するか、FatJarやOneJar[3]という、複数のJarファイルを一つにまとめるツールを使うことで解決できる。

ライセンス[編集]

サン・マイクロシステムズ(サン)のJavaの財産的価値はフリーソフトウェアコミュニティで議論を呼んだ。なぜならばサンのJavaの実装はフリーソフトウェアではなかったため、フリーソフトウェアや主にDebian$100ラップトップFedora CoreのようなGPL互換ライセンスが要求されるプロジェクトにはサンのJavaを含めることはできなかった[4][5]

サンは JavaOne 2006 で、Javaはオープンソースソフトウェアになるだろうと公表した。この声明はSun Softwareの上級副社長Rich Greenによって公表された。「オープンソースソフトウェアにするか否かという問題はない。どのような方法でオープンソースにするかが問題である。つまり我々はこれを実行する。」[6]

2006年7月には、サンの最高技術責任者Robert BrewinはJavaは2007年6月に部分的にオープンソースになるが、全体のプラットフォームが完全なオープンソースになるには時間がかかるだろうとコメントしている[7]

2006年11月13日、サンは標準版Java実行環境は2007年3月にGPLの下でリリースされる予定であることを公表した[8] Java実行環境のソースコードはGPLの下で利用可能になる予定である。リチャード・ストールマンによると、これはJavaの罠の終焉を意味するとのことである。マーク・シャトルワースはJavaのオープンソース化についての最初のSunによる発表を「フリーソフトウェアコミュニティにとっての確かなマイルストーン」と評価した。[8][9]

解決策・代替案[編集]

Javaのライセンスの問題は徐々に解決されつつある。時間が解決していると言える面もあるといえる。今後も新たなライセンスの問題に直面しそうであれば、Java Community Process に提案するよう働きかけてみるという手段もある。フリーソフトウェア財団Eclipse Foundation など他のオープンソースコミュニティの助けを借りることで問題を解決に向けて進めることができる可能性がある。

リソース管理[編集]

Javaはメモリを管理するが、他のリソース(JDBCデータベース接続など)は管理しない。これらはC++メモリを解放する必要があったのと同様、プログラマによって解放されなければならない。

メモリ管理[編集]

Javaはガベージコレクションによるメモリ管理機能を備える。これによって、完全ではないもののプログラマがメモリリークを起こすようなプログラムを作ってしまう危険性を減らした。Javaではオブジェクトは常にヒープ領域に(JITコンパイラによって最適化されてスタックレジスタに割り当てられない限り)確保される。ローカル変数スタックレジスタに確保される。C++ではオブジェクトをヒープ領域に割り当てるかスタック領域に割り当てるかをプログラマが選択することができたが、Javaではそれが不可能になっている。

オブジェクトはガベージコレクションによって管理されるが、Javaはプログラマにガベージコレクションがいつ起こるかを保証しない。たとえSystem.gc()を用いてもプログラマはガベージコレクションを阻止することも任意のオブジェクトを解放することもできない。これはプログラミングを簡易にしメモリリークの可能性を軽減するが、より効果的なメモリ処理を行うための柔軟性が犠牲になっている。Cアセンブリのような低水準言語はこの柔軟性を提供する。

C++などで書かれた多くのプログラムはメモリリークの犠牲になりがちだが、問題はメモリリークだけではない。ファイルハンドルやデータベース、ネットワーク接続のような他のリソースのリークは特に例外が投げられたときに常に起こりうる。C++ではRAIIと呼ばれるイディオムによっていずれの問題も克服することができるが、Javaプログラマは忘れずにfinally節でリソースを解放する必要があり、Javaが何を解放するかということとプログラマは何を解放しなければならないのかということをきちんと理解する必要がある。

解決策・代替案[編集]

この問題は、メモリを増設する、最大ヒープメモリサイズを拡大するなどで解決できるケースもある。弱い参照 (SoftReference) を用いることで多少の解決策になることはある。だが、JREJDKのバージョンが特定の古いものであることが要因となっていることもある。最新版のJREやJDKで実行、開発すれば問題発生率を下げることもできる。

finally節は、次世代のJava (Java SE 7) で利用可能になる可能性があるクロージャで解決できることもある。これらは、場合によっては、ライブラリフレームワークによって解決できることもある。ファイル入出力の場合、Apache Commons IO、データベース接続のときはApache Commons DBUtils、HibernateやApache Cayenneなどのオブジェクト関係マッピングフレームワークを用いることでfinally節のことをあまり多く気にしなくても良いようにする手段がある。

言語選択[編集]

プリミティブ対オブジェクト / オートボクシング[編集]

Javaの設計者らは、現在他の言語にあるいくつかの機能(多重継承演算子多重定義タプルなど)を実装しないことを決めた。

総称型ジェネリクス)がJava 5.0に導入されたとき、すでにJavaにはクラスの大規模な枠組みがあった(それらの多くはすでに非推奨となっていた)、そして後方互換性を保つため、総称型の実装方法として既存のクラスを維持することを可能にする(コンパイル時の)型消去(型削除、type erasure)が選ばれた。これは、他の言語と比べると、総称型の導入によって提供される機能を限定してしまう結果になった[10][11]

Javaのプリミティブ型はオブジェクトではない。プリミティブ型は値への参照ではなく値そのものをスタック領域に保持している。これはパフォーマンスの理由により行われた。このためJavaは純粋なオブジェクト指向言語と見なされていない。またこのことによりリフレクションがより複雑になっている。しかし、Java 5.0は、コンパイル中に要求された場合にプリミティブ型を対応するプリミティブラッパークラスのオブジェクトに自動変換する機能(オートボクシング)をサポートしている。オートボクシングではNullPointerExceptionが投げられる可能性がある。オートボクシングは暗黙のうちに起こる(キャストメソッド呼び出しを伴わない)ため、このNullPointerExceptionという非チェック例外はJavaプログラムのコードに目を通しただけでは明確にはならない恐れがある。

非virtualメソッド[編集]

Javaはメソッドを非virtualにする手段を提供しない(オーバーライドを禁止するためにfinal修飾子を使用することで封印することはできる)。これは派生クラスに、同じ名前の関係の無いメソッドを定義させる方法がないことを意味する。これは基底クラスが別の人間によって設計されるときに問題となることがあり、また、新しいバージョンが、派生クラスで同じ名前のメソッドがすでに存在するときに、同じ名前とシグネチャのメソッドを導入することで問題となることがある。これは、どちらのクラスの設計者の意図にも反して、派生クラスのメソッドが基底クラスのメソッドを暗黙のうちにオーバーライドするであろうことを意味する。これらのバージョン問題にある程度適合するためにJava 5.0は@Overrideアノテーションを導入しているが、後方互換性を保つには、それをデフォルトでは強制できない。

単一パラダイム[編集]

Javaは主に単一パラダイム言語である。Java 5.0に登場したstatic importsの追加はこれまでのJavaよりも手続きパラダイムによりよく順応する。

例外処理[編集]

JavaではC++でオプションとされていた例外処理の仕様を取り込んだが、この際チェック対象の例外に対応するthrows文を必須とした。例外のチェックは小規模なシステムにとっては役立つが、大規模なシステムについても有益であるかどうかについては統一的な見解には至っていない。特に上位のコードでは下位のコードから発生するエラーを意識したくない(例としては、名前解決例外であるNamingException)。名前クラスの作成者は名前解決例外をチェック対象の例外として上位コードに対応を強制するか、コンパイル時のチェックを使わずに下位のコードからの例外を連鎖的に通知するかを選択する必要がある。

クロージャ[編集]

Javaでは、匿名内部クラスを使用する事で、基本的なクロージャに近い記述が実現できる。しかしこれは完全ではなく、匿名内部クラス外の変数に対する参照をクラスのフィールドとしてfinal修飾子付きで宣言する必要がある。これは、変数のスコープを抜けたときに削除できるような、様々な寿命に対応したスタックモデルをJVM実装者が選択出来るようにするためである。また匿名内部クラスの構文も冗長であり、例えば"Runnable"インターフェースに対して匿名内部クラスを定義する場合、単純なコードブロックとして定義する事は出来ず、インターフェースの"run"メソッドを実装する様に定義する必要がある。

浮動小数点演算[編集]

Javaの浮動小数点演算は主にIEEE 754(二進化浮動小数点数演算標準)をベースとしているが、例えばIEEE 754に必須とされる例外フラグや方向指定の丸めなどの、いくつかの機能については"strictfp"修飾子を指定した場合でもサポートされない。Javaの仕様として知られているものはJava自体の問題ではないが、浮動小数点数演算を行う上で避けて通れない問題である[12][13]

ルックアンドフィール[編集]

Swingプラットフォームを使って、Javaで書かれたアプリケーションのグラフィカルユーザインタフェース (GUI) のルック・アンド・フィール(Look and feel、直訳すると「外観と操作感」)は、大抵ネイティブのアプリケーションのものと異なる。プログラマはネイティブのウィジェットを表示するAWT(ネイティブであるためOSのプラットフォームと同じ見た目を提供する)を使うことを選択することができる。しかしAWTツールキットは、高度なウィジェットのラッピングを必要としかつ様々なサポートされたプラットフォームで移植性を犠牲にしない高度なGUIプログラミングには、向いていない。そして、SwingとAWTにはとりわけ高水準ウィジェットにおいてAPIが大きく異なる。

Swingツールキットは全てJavaで実装されている。Swingツールキットはネイティブアプリケーションとは異なるルックアンドフィールを持つという問題がある。一方でSwingツールキットのウィジェットはネイティブなツールキットの機能に限定されないという利点がある。この利点は全てのプラットフォームで利用できることが保証される最も基本的な描画機構を使ってウィジェットを再実装していることによる。不幸にも、(2006年8月の時点で)Java実行環境 (JRE) のデフォルトのインストールでは、デフォルトの埋め込みMetal(メタル) ルックアンドフィールの代わりに、システムの「ネイティブ」なルックアンドフィールを使わなかった。プログラマがネイティブなルックアンドフィールを設定するようにしないならば、ユーザーは外観がネイティブアプリケーションのそれとは非常に異なるアプリケーションに遭遇するだろう。 Mac OS Xの配布元に限って含まれる アップル自身のJava実行環境の最適化バージョンは、デフォルトで「デフォルト」をセットし、"Aqua" のルックアンドフィールを実装し、Macintosh上のSwingアプリケーションは、ネイティブなソフトウェアに似た外観を提供している。Mac OS Xの環境でさえ、プログラマはそのアプリケーションがAquaのように見えることを確実とするために、若干の余分の仕事をまだしなければならない(例えば、それらはメニューバーがWindowsのようにアプリケーションウィンドウ内部ではなくMac OS Xメニューバー内部に表示させるために、システムプロパティをセットする必要がある)。

解決策・代替案[編集]

この問題は、開発者がJavaのバージョンを Java SE 6以降にアップグレードすることで解決する。Java SE 6になってからJavaのデスクトップまわり、GUI環境は一新され、開発効率は高まっているため、以前のバージョンよりも開発の手間はかからなくなっている。

パフォーマンス[編集]

一般[編集]

Javaプログラムのパフォーマンスに関して一般論を述べることは不可能である。なぜなら、実行時の性能は、言語自身が持つ固有の特性よりも、JavaコンパイラJava仮想マシンの品質に非常に大きく影響されるからである。Javaバイトコードの実行方法は、仮想マシンによって実行時に翻訳して実行する方法と、ロード時もしくは実行時に機械語にコンパイルしてハードウェアによって直接的に実行する方法がある。前者の翻訳実行する方法は機械語の実行よりも遅く、後者のロード時もしくは実行時にコンパイルして実行する方法は、最初のコンパイル時にパフォーマンスが犠牲になる。

この問題は現在に於いては、さほど大きな障壁にはなっていない。なぜならば、コンピュータの性能はムーアの法則によって刻一刻と進化しているからである。そしてJavaもバージョンアップするにつれて最適化技術を進歩させてきた。これによって、Javaの初回起動時のオーバーヘッドは誤差の範囲内になりつつある。C++ではJavaよりも高速にできるよう手動で最適化ができるが、C++でJavaの実行時最適化技術を真似することは困難である。

言語仕様による制約[編集]

Javaだけに限ったことではないが、パフォーマンス上の障壁となる言語仕様がいくつか存在する。それは例えば配列境界チェックや実行時型チェック、仮想関数などである(コンパイラ時の最適化で解消できるものもある)。また、言語仕様の欠落がパフォーマンスに影響する場合もある。例えば、Javaは構造体の配列や真の多次元配列を持たず、オブジェクト(や配列)への参照の配列が定義できるのみである。また、Javaでは関数から複数の値を返すためにはオブジェクトを使用する以外の方法がない。これらによってJavaのプログラムコードは、他の言語で書かれたコードよりも頻繁にヒープを使用しなければならなくなっている。

ガベージコレクション[編集]

不要オブジェクトの自動回収を行うガベージコレクションは明示的なメモリ解放に比べそのオーバーヘッドは大きいが、ガベージコレクタの実装やアプリケーションでのオブジェクトの利用状況によってその影響はまちまちに変化する。多くのJava仮想マシンは世代別ガベージコレクションの採用によって動的メモリ管理を高速化しているため、多くのアプリケーションでは高い性能を示している。

バイトコード対ネイティブコンパイル[編集]

 一般に、プログラミング言語とその実行方法は、直行しないのが普通で、コンパイラだったり、インタプリタだったり、バイトコード方式だったりする実装があっても、構わないはずである。しかし, Java には、実装上制限があり、あまりに自由度がない。

ジャストインタイムコンパイル(JITコンパイル)とネイティブコンパイルの性能差はほとんど無いとされるが、よく議論の種にもなる。JITコンパイルには時間が掛かるため、短時間で終了するアプリケーションや巨大なプログラムでは問題となる。しかし、一旦ネイティブコードに変換すれば数値計算においてもネイティブコンパイルと同等の性能を示す。

Javaはメソッド呼び出しの明示的なインライン化をサポートしないものの、多くのJITコンパイラではバイトコード読み込み時にインライン展開を行い、また実行時のプロファイリングを利用してその効率をより高めている。HotSpot仮想マシンが採用している動的再コンパイルでは、実行時でしか取得できない情報を利用することで、多くのプログラミング言語が採用する静的コンパイル方式を超える性能を得る場合もある。

ハードウェアインタフェース[編集]

Javaはセキュリティと移植性を重視して設計されたので、Javaではマシンアーキテクチャとアドレス空間への直接アクセスをサポートしていない。スキャナデジタルカメラ、オーディオレコーダ、ビデオキャプチャないし実質的に直接メモリ空間の制御(一般的に、ドライバインストールを必要とするハードウェアやそれらの部品)を必要とする特定の一部のハードウェアを動かすことはJavaでは容易に実現できないことを意味する。この問題の実例はJavaのバージョン1.0で見られた。様々なプリンタドライバへのインタフェースコードがこの初期のJava実行環境に含まれず、プリンタへのアクセスが可能でなかった。

ネイティブコードとインタフェース[編集]

ハードウェアに直にアクセスするクライアントサイドまたはサーバシステムは、ネイティブコードとJavaライブラリを橋渡しするJava Native Interface (JNI) を使って、C、C++およびアセンブリ言語とJavaを組み合わせる方法を取る。また、ハードウェアアクセスを担うネイティブコードとJavaとの通信をファイルやデータベース、共有メモリを介して行う方法もあるが、理想的なやり方ではない。

JNIを使うことで、プラットフォーム依存性、潜在的なデッドロックメモリリーク、場合によっては性能の著しい劣化などの問題が発生しうる。また、2つの異なるコードベースをメンテナンスするために必要となるコードの複雑性は、言うまでもない。しかし、それは、例えば.NET Framework 共通言語ランタイムのように、他の仮想マシン言語と共通する事例である(Platform Invocation Servicesを参照)。

解決策・代替案[編集]

現在では今のところ、Javaはまだまだハードウェア開発、デバイスドライバ開発には適していない点が多い。この問題について、JavaでUSBドライバ開発ができる Java Communication API という技術がすでにある。他にも、Jini対応機器を端末に接続すると、サーバから自動的にJava製ドライバを端末にダウンロードしてその機器を使うことができる技術Jiniというものが考えられている(JiniはしばしばJNI (Java Native Interface) と誤読することがあるので要注意である)。

一貫性のないJava仮想マシン実装[編集]

Javaは、Java仮想マシン (JVM) の上部で動くバイトコード言語である。異なるプラットフォームで実行できる能力と言語の互換性は、最終的にはJVM実装の安定性とJVMのバージョンに依存している。Javaは非常に様々なシステム上で動くとうたわれているが、最新のJVM(とJRE)はWindows、LinuxSolaris対応だけ活発に更新されている状況である。HP-UX(Java for HP-UXなど)とIBM (for MVS、AIX、OS/400) は、それぞれのプラットフォームファミリー独自の実装を提供するが、必ずしも最新のサン・マイクロシステムズのリリースを反映していない。他のプラットフォームにおけるJVM実装も、たいてい今後も続くが、時々一般の実装例よりも何年か何ヶ月か遅れるので、互換性問題が生じる。具体的な例を示すと、Java 2 Platform Standard Edition 1.5 (J2SE 1.5) をサン・マイクロシステムズがリリースしたのは2004年9月30日[14]だが、Mac OS X用J2SE 1.5をアップルが公開したのは2005年3月[15]であり、5か月余りの差が生じている。

移植性・互換性[編集]

"Write once, run anywhere"(WORA、一度書けばどこでも動く)という言葉があるとおり、Javaの目標の一つにプラットフォーム非依存があげられる。JavaコンパイラJava仮想マシン用の中間言語Javaバイトコード)を生成する。コンパイルされたJavaのプログラムは、Java仮想マシンを実行環境として動作する。この仮想マシンがハードウェア間の差異を吸収することで、プラットフォーム非依存を実現している。ただし、現時点では一部にプラットフォーム依存の部分があり、完全なプラットフォーム非依存ではない。

また、マルチプラットフォームにするということは、一部のプラットフォームにしかない独自の機能はJavaから使えないことを意味する。 例えばWindows用グラフィックス APIDirectX はJavaから直接呼び出すことは出来ない。そのため、橋渡しをするための拡張APIが提供されている。

またJavaではバージョン間の下位互換性・上位互換性の問題が議論の対象になっている。Javaではバージョン間の互換性をある程度の水準まで達成している。しかし、バージョンの異なる実行環境の取り扱いには課題が残っている。例えばJ2SE 1.4実行環境用に書かれたプログラムは、実行環境にJ2SE 1.3を想定すると明示的に指定してコンパイルしなければJ2SE 1.3実行環境では動かず、利用するライブラリがJ2SE 1.4以降から追加されたものである場合にはJ2SE 1.3実行環境での実行を諦めなければならない。J2SE 1.3からの上位互換性は、J2SE 5.0まで保証されている。J2SE 1.3以降のJavaプログラムでは下位互換性は保証されないが、Java実行環境 (JRE) の自動アップデート機能によって仮想マシンを最新バージョンにアップデートすれば解決できる。JDK 1.1、J2SE 1.2 時代のJavaプログラムは、現在となっては古いため、上位互換性問題に引っかかる可能性がある。その場合は、そのJavaプログラムを作った者に最新版のJavaコンパイラでもコンパイルが通るように直してもらうしかない。

解決策・代替案[編集]

この問題も、サン・マイクロシステムズのJavaコーディング規約をJavaプログラマが守っていればほぼ起きることがなく、Javaソースコード上のimport宣言や新しく加わったJavaキーワード(enumやassert)と重複するものが無ければほぼ心配することもなくなる。とくに、多くの場合において、これらの問題はコーディング規約だけでなく、統合開発環境CheckStyleFindBugsなど各種ツールなどによって解決できるケースがある。Javaプログラマは、日頃からCheckStyleやFindBugsを使ってプログラミングしていれば、上位互換性の問題につき当たる可能性は下がる。

関連項目[編集]

[編集]

[ヘルプ]
  1. ^ a b Setting the class path” (Windows). Java SE 6 Documentation, JDK Development Tools Reference. Sun Microsystems. Accessed 2007-01-27.
  2. ^ a b Setting the class path” (Solaris and Linux). Java SE 6 Documentation, JDK Development Tools Reference. Sun Microsystems. Accessed 2007-01-27.
  3. ^ developerWorks > Java technology > One-JARでアプリケーションの配布を単純化するカスタムのクラスローダーによるパワー・プログラミング
  4. ^ さらに、一部の専門家はJava SCSLライセンスは誰もがJavaのフリーソフトウェアクリーンルーム実装に貢献するために著作権を放棄するライセンス条項に同意することを要求していると主張している。しかしながらサン・マイクロシステムズの人々は、改訂されたJavaライセンス条項の下ではもはや問題ではなくなったと主張している。"What full-fledged Java development platforms are available in Debian?"”. "Debian FAQ". 2006年12月8日閲覧。
  5. ^ Java SE Tainting Issues”. "Java.net: The Source for Java Technology Collaboration. 2006年12月9日閲覧。.
  6. ^ Galli, Peter (2006年5月16日). “Open-Source Java? Not If but How”. eWeek. 2006年12月9日閲覧。. サンの論点は彼らが非互換分岐なしにオープンソースJavaを欲しがる方法であった Phipps, Simon (2006年5月26日). “Forks Aren't A Problem”. 2006年12月9日閲覧。。 これは次のJavaOneでサンの最高経営責任者を務めるジョナサン・シュワルツのコメントにより説明される可能性がある。彼のブログではこう語っている。「我々はオープンソースJavaの厳粛な前進を行っているが(GPLライセンスが議論中であるというは皮肉にもかかわらず)、何が一番大事であるかに議論の焦点をあてている。一番大事なことはソースコードを入手する機会ではない(ソースコードはすでに広く利用可能である)。一番大事なことは互換性を確固たるものとすることである。」 Schwartz, Jonathan (2006年6月25日). “60 Days into the Job…”. 2006年12月9日閲覧。.
  7. ^ Krill, Paul (2006年6月24日). “Sun CTO: Incremental open-sourcing of Java is the way”. Computer World. 2006年12月9日閲覧。
  8. ^ a b Sun Opens Java”. Sun Microsystems (2006年11月13日). 2006年12月9日閲覧。
  9. ^ Sun 'releases' Java to the world”. BBC News (2006年11月13日). 2006年12月9日閲覧。
  10. ^ Generics in Java”. Object Computing, Inc.. 2006年12月9日閲覧。
  11. ^ What's Wrong With Java: Type Erasure” (2006年12月6日). 2006年12月9日閲覧。
  12. ^ Kahan, W.; Joseph D. Darcy (1998年3月1日). “How Java's Floating-Point Hurts Everyone Everywhere (PDF)”. 2006年12月9日閲覧。
  13. ^ Types, Values, and Variables”. Sun Microsystems. 2006年12月9日閲覧。
  14. ^ Sun Microsystems, Inc. (2004年9月30日). “Sun Ships New Version of Java Platform”. 2007年10月15日閲覧。
  15. ^ Apple Inc. (2005年3月16日). “About Java 2 Platform Standard Edition (J2SE) 5.0 Release 1 for Tiger”. 2007年10月15日閲覧。

外部リンク[編集]