Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9707

【雑談】あえて今、20年前のJavaを自由に語る

$
0
0
はじめに 「10年前の技術」に関する記事。 こんなテーマなら、さらに20年前の話から始めて、私の思い出話を語ろうかなと思う。 この記事では、私が知っている時代の「Java」を自由に語る。 語ろうとしている年代(1990年代後半)、私は当時小学生であり、当然SEなんて仕事はしていなかったが、密かに「システムエンジニアになりたい」という夢を抱いていた。 そんな私にとってJavaは、「プログラミング言語として学んだ2番目の言語」であり、 長い期間きちんと独学していた言語でもあるので、思い入れがたくさんある。 当時からしてみれば、オブジェクト指向やガベージコレクション、AWT/SwingなどのGUIライブラリ(なお今は死語)は非常に画期的な機能の一つであった。 当時は「Javaは文法的にもかっちょいいし、Cよりは遅いけど色々ソフトウエアが作れる言語なんだぜ~」なんて世間の風潮だったわけ。 実際、当時はJavaアプリケーションやJavaアプレット(これも死語だな)が当時人気だったFlash並みにWebで量産されていき、人気があった。 そんな今、そんな姿はどこへやら、既得権益(Oracle社)に飲まれた言語となっている。 ところで、当時あれほど「ポンコツ」と称されていた(Javaとなんとなく似ているけど全く別物の)スクリプト言語JavaScriptは、今や「開発者が好む言語No.1」になる時代。 まさかこんな時代が来るなんて想像もしていなかった。 注意 ここに出てくるコードは、「20年前~15年前くらいのJavaのコード(を何となく思い出しながら書いたもの)」であり、化石のようなものだ。まず現代の設計に追いつかないものであるので、決して使用してはならない。 歴史に関する詳しい部分は不正確なので、例えば以下の良記事を参照にすること。当時の私は中学生~大学時代で、リアルで仕事しているわけではない。ビジネスや規格競争のような難しいことはようわからずにやってたからな。 本人も当時の状況を他の資料(Wikipediaなど)を使って割と不正確に伝えていますので、「これおかしい」とか「ここもっと調べて欲しい」ってのがあったら是非コメントください。 旧き良きJavaの遺産 20年前、Javaの黎明期 Duke 読者は、こいつの存在「Duke」を知っているだろうか。 ちなみに最近まで名前を知らなかった このマスコットキャラクター、実は既にJDK Beta(1995)の頃には誕生していた。 当然のことながら、よく売れている書籍の中でこいつが描かれていたら、間違いなくJavaの書籍であることを確実に伝えている。 どうやってJavaを勉強したか 私がJavaを勉強したのは、丁度JDK 1.0~1.1くらいの内容をまとめてくれたプログラマーの書籍からである。 今でも間違いなく現存しているホームページに「浅煎り珈琲 Javaアプリケーション入門」がある。(なお化石) 今でこそ、無料有料問わず色んな分野に対する書籍・Webサイト・動画が多量にある時代であるが、 当時の私からしてみれば、プログラミングの基本がここまできっちり理解できるコンテンツは貴重で、 私にとってのデータアーキテクチャの基礎を今でも支えてくれている。 家電とJava 実は開発当初、JavaはIoTの先駆けだったことをご存知だろうか? Javaは、当時として堅牢性を考慮した設計であり、同時にWeb技術への拡張性を兼ね合わせているため、 これをWebSocketを使ってHTTP/HTTPS/FTPプロトコルなどに接続してしまえば、あら不思議IoTモジュールの完成、そんな未来を描いた言語だったのだ。 Web技術の中心となった現代のJavaであるが、Javaは、元々家電の制御のために作られた技術であった。 それは、当時C++はあまりにも柔軟性がありすぎて制御の安定性に問題があり、現代ほど電気製品の制御に向いている技術ではなかったためである。 そこで、JavaはC++の概念を整備して堅牢性を重視して設計された。 Javaの概念は型制約の厳しさを残してC#に引き継がれている。 Java仮想マシン(JVM)と互換性 Java仮想マシンという概念も、現在で言うところのクロスプラットフォーム開発やVirtual Desktop、Dockerなどの仮想化環境の考え方のベースとなる素晴らしいものであった。 一般に、プログラムの実行される方式はインタープリタ方式とコンパイラ方式がある。 インタープリタ方式は、人間の書いたコードをそのまま実行する方式 コンパイラ方式は、人間の書いたコードを機械語に変換する方式 インタープリタ方式が現在の多数の高級言語に該当し、 コンパイラ方式の代表格がC++であることは言うまでもない。 そして、そのJavaは、どちらの方式でもない中間コード方式を取っていた。 これはコンパイルするときに、Java仮想マシン(JVM)に理解できる形式で変換するものであり、 それで生成された中間言語は、JVMが責任を以て実行するので、どのような環境でも実行出来る強みがあったのだ。 当然、家電のような組込み用途においても、このような設計が活用できるのが有意義には違いない。 Swing GUIに関しても規格化が行なわれるような流れとなっていて、JavaにおいてはSwingと呼ばれるライブラリが並んだ。 当時としてはLook&Feelに配慮された綺麗なGUIで、JVMの性質上どのようなプラットフォームでも動くのが強みだった。 実際、Swingの画面とmfcの画面を比較してもらえれば分かると思う。 mfcはC++でWindowsでしか書けないのに、よくも悪くもWindows的なGUIであるのに対し、Swingはこれがあらゆるプラットフォームで動くんだからすごいすごいってなると思う。 Swing GUIの規格化とレイアウトマネージャー さらに、このSwingとはちょっと違う話題だが、レイアウトマネージャの概念も、Javaをユニークにする一つの概念であった。 実はレイアウトマネージャに関してはきちんと対応したC#フレームワークは、WPFまで存在しないのだ。 つまり、当時のWindowsアプリケーションはほぼ例外なく絶対座標系でGUIを表示していた。 現在でもこの絶対座標系を禁止するような正式な解決策はなく、私もWindowsアプリ捨ててブラウザ使いたいって気持ちになってしまう。WPFめんどくせえんだよなあ そしてそのWPFも難しすぎるってんで、それをHTMLとJavaScriptで記載してアプリケーションを作れるようにしたelectron.jsが割といま元気であるけれども、こうした時代に来るまでMicrosoftで作られた製品がユニバーサルデザインを意識した設計になることはなかったのである。 (tkinterやQtを見ていると、もしかすると当時でもユニバーサルデザインに対応したものはあったかもしれませんが。) 特に、以下のようなGridBagLayoutと呼ばれるレイアウトは難しかったが非常に便利であった。 HTMLで言うところの <table> <tr> <td colspan=2>AA</td><td>BB</td> <td>1</td><td>2</td><td>3</td> </tr> </table> に対応している機能なのだが、多少その仕組みが複雑とは言えどのようなレイアウトも再現出来たのには一定の効果があったように思える。 また、同じGUIをカードを切るように前後ろに配置するCardLayoutと呼ばれるレイアウトも便利であった。 これも、現在のHTML5ではインラインで代用される概念だと思うが、こういうのがデフォルトについていたJavaはレイアウトの考え方に対しても素晴らしい部分があったと思う。 アプレットとFlash この時代を象徴するJavaで出来たものと言えばアプレットであるだろう。 けれど、実はJavaの中でアプレットは初期の頃はJavaの目玉技術として採用されていたものの、2017年にはもはや表示されなくなるなどの憂き目に遭っている。 それでも、当時はすごかったのだ。個人のHPにJavaアプレットが大量に生成された。 今ではJavaScriptを使えば容易にゲームくらい作れる時代(私にそんな力はないが)だが、当時流行したFlash同様、JavaはプラグインをHTMLに埋め込む形式として流行した。 ↑のような本が山ほどあった時代だったわけです Javaアプレットの最大の強みは、JVMが責任もってブラウザの動きを管理してくれたことになる。 今でこそフロントエンド⇔バックエンドという用語は一般的なものになっているが、当時こうしたゲームを作ろうと思ったらプラグインを埋め込む方式が主流なのであった。つまり基本はフロントエンド側で完結するスタイルなのである。 そんなアプレットのHTMLは、どこでもこんな感じに書籍に書いてあったわけだ。 <html> <title>Java Applet</title> <body> <applet src="myapplet.class" width=640 height=480> </applet> </body> </html> この書き方自体、今のJavaScriptのHTML5に通ずるところがある。 もちろんこんな感じでプラグインのプログラムを1個だけ設置するなんてのはないのだが、idやname、classを使って上手いこと識別子を与えて、そこに表示したいプログラムをダイレクトに組み入れる考え方は似通っているのだ。 Collection Framework Collection FrameworkはC++のSTLを真似したものであるが、これもJavaでは独自の仕様となった。 実際、JavaのSTLで採用されているVectorはC++をベースに設定された可変長配列であり、基本になっているようである。 ところが、当時Javaはジェネリクスが存在しなかった。 一応、可変長配列として有名なArrayListなんか ArrayList list = new ArrayList(); と書いていたのだが、型指定の難しさは課題にあったのだ。それで2005年のJ2SE 5.0以降のアップデートでジェネリクスが生まれていた。 ちなみにそういう面では、他言語とは多少出遅れている面もあったと思われる・・・と思いきや、全く同じ時期の2005年にC# 2.0が出来たのだが、この時点でC#はジェネリクスに対応していたようだ。 私の中では、C#でジェネリクスを積極的に扱えるようになったのは、LINQが誕生するC# 3.0だったように思える。 革命的な文法 (1) インスタンスを渡す インスタンスの概念は、C++の左辺値参照が基本となっている。そしてその左辺値参照はどうも1988年に構想され導入されたもので、この概念自体はC++がベースとなっているようだ。 しかし、C++の左辺値参照には「インスタンス」の概念はなく、実体がどこかにいなければそのデータは無効となる。 参照渡し(C++) // originalはSomeClass型の実体のあるデータ // 参照は常に実体を持たないといけない。 SomeClass& obj_ref = original; またインスタンスに近い概念であるポインタは、nullptrを許容するのでそれが制御上の問題に繋がる。 ポインタ渡し(C++) // nullptrが許容されてしまう SomeClass* obj_ref = nullptr; // つまり、途中でこのような処理がなければ実行時例外が起きる if( obj_ref == nullptr) { ...(処理)... } // originalはSomeClass型の実体のあるデータ // 参照は常に実体を持たないといけない。 obj_ref = &original; ところで、Javaのルールに基づいてインスタンスを発明したことで、抜本的にこの問題が解決されているのだ。 つまり、インスタンスはそこでは常に参照を渡し続けると出来るのだ。 インスタンスを渡す SomeClass obj = new SomeClass(); // ☆ obj2を生成するときに、objを参照渡し SomeClass2 obj2 = new SomeClass2(obj); だが私の印象だが、これが原因で、当時、C言語/C++を中心に開発してきたエンジニアは、JavaやC#、objective-Cなどのオブジェクト指向型の言語で挫折する傾向が強かったように思う。 ☆の部分で、まさかobjが変更される可能性があるとは思うまい。C++の言語的には、 SomeClass2* obj2 = new SomeClass2(obj); と書かれりゃ、 class SomeClass2 { public: SomeClass2(SomeClass obj) { ...(処理)... } ...(以下省略) }; と勘違いする人と、 class SomeClass2 { public: SomeClass2(const SomeClass& obj) { ...(処理)... } ...(以下省略) }; と何となく意識づけ出来る人と分かれてしまうからだ。 その対策として、C#では値クラスが導入されたり、C++でも構造体とクラスは別の役割をするように調整されたりしたわけだが、私の印象ではバリバリポインタを使ってくるエンジニアほど、ここのところは弱いという印象があった。 一つの障壁になってしまっていたのが、悲しい。 革命的な文法 (2) インタフェースとポリモーフィズム また、Javaにおいてインタフェースクラスも重要な文法表現である。 C++では、多重継承がサポートされている。 class SubClass : public SuperClassA, public SuperClassB { ...(実装)... }; この多重継承の最大の利点は、様々なクラスの型変換を許容することにある。このように書けば、 // originalはSuperClassAで生成されたオブジェクト auto obj1 = dynamic_cast<SuperClassA*>(original); と書くことを許容するが、このdynamic_castはSuperClassB*に対してはnullptrを返却する。つまり、型変換に対しての規則性が破綻しているのだ。 それをJavaでは、 一般的なクラスの多重継承は禁止とする。 多重継承はインタフェースのみ許容し、インタフェースに存在するメソッドは全て抽象化され、オーバーライドしないといけない。 のルールを追加した。このようにしたことで、 interface IntarfaceA { ...(実装)... } interface IntarfaceB { ...(実装)... } public class SubClass extends SuperClass implements InterfaceA, interfaceB { ...(実装)... } と書けるようになり、さらにポリモーフィズムに破綻せずに、 // originalはSubClass型のインスタンス interfaceA obj_a = original; interfaceB obj_b = original; とする記載を許容することが出来たのだ。 これは大きなポイントであり、古きJavaを語る上でマルチスレッドやEventListenerを使うときに重要な部分となった。 EventListener型の発明 ~ Delegate型との大きな違い ~ このEventListenerを聞くのは、現在では一部のJavaScriptネイティブでやっている人しかいないと思われるが、これも非常に画期的な仕組みだった。どう画期的かと言うと、イベントリスナークラスを定義してそのGUIに登録するだけでイベントの挙動を表せてしまうわけだ。 確かに、この仕組みはややこしいものであったが、このことによりイベントを共通のオブジェクト化する考え方が一般的になったのもある。 GUIにActionListenerと呼ばれるインタフェースを用意しておき、そのインタフェースを継承したクラスを開発者が作り、それを登録する。 例えば同期のC#ではこうした設計をするとき、delegateを採用するだろう。Windows Formsの例であるが public partial class FormMain : Form { public FormMain() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { MessageBox.Show("ボタンがクリックされました!"); } } という感じで、あくまでデザインビューワの中にbutton1_Clickを仕込ませて構成する。 ここで、フォームに存在するボタンGUIは別にメソッドOnClickを用意しておき、実際はdelegateでbutton1_Clickを実行するように仕事を投げる。 この仕組みは一見快適で、逆にWindowsアプリケーションの仕組みが楽すぎて、他のフレームワークに移行できない人が増えている原因になっているのだが、もう一つ大きな問題を抱えている。 それは「Formの実装の中に処理が潜んでしまうこと」だ。 つまりこうしたコードを書いてしまうと、MVPパターンで言うところのフォームのViewの中に、ボタンイベントであるPresenterの処理が混在してしまう。 技量がなくモジュール分割の習慣がついていないエンジニアなら、下手したらそのPresenterの中にModelとViewを一緒にごちゃ混ぜにして書く惨事すら招くのだ。もはやそんな状況では、MVPモデルなんて言うものは存在しないだろう。 それをJavaではEventListenerの形式で回避していて、 リスナークラス class MyActionListener implements ActionListener { MyApplet applet; public MyActionListener(MyApplet applet) { this.applet = applet; } //ActionListener インタフェースの実装 public void actionPerformed(ActionEvent e) { ...(Modelの処理やViewのプロパティに該当する値を変更する)... } } GUIクラス public class MyApplet extends Applet { private Button btnBlue; private MyActionListener actionListener; ... (略)... public void init() { btnBlue = new Button("blue"); actionListener = new MyActionListener(this); btnBlue.addActionListener(eh); } } こうすることで、少なくとも(Viewの一部+Modelの一部の処理)をListenerが、(Viewの処理)をAppletが担ってくれるようになるので、 設計が分かっていれば少しは上手く出来るようになる。 ただ、多分MVPモデルをきちっと意識して設計した人がいたかはかなり怪しい。解説書を読んでもそういうことには触れなかった。 実際、WPFが出てからMVVMに対する布教が進んできたという印象があるので、当時のプログラマーも要素分割の概念があったかはちょっと分からない。私は(当時)あくまでホビーユースだったから 10年前の自分に伝えたいこと 実は、こうした20年前の技術があったり、色々と画期的な設計を目指したJavaであったが、ある時期からそのJavaをやるのをやめたのであった。 その理由のきっかけは色々あるが、 Java 6からのアップデートがあまり行われなかったこと プラグインを使う方法がセキュリティセーフではなかったこと Oracle社の買収 などが挙げられる。 まあ、アルゴリズムの分野に関して言えばJavaで作られた成果物を結構見るし、それなりに上手く動くので、教育的な部分に関してはそれなりに使えた面はあったけれど、既に当時から限界な感じはちょっと見えていたのであった。 また、Javaを躍進させたモバイル開発の業界は確かに今も堅調だと思うのだけど、 少なくともPCを中心にやっていく私の世界だと、当面手を引かないといけない状況になっていたのも事実だ。 それが現在、安定期も終わり徐々に衰退期に近づいているような。 Oracle社が(アップデートを重ねることで)独自のDBシステムと共に既得権益化したこと、 openjdkはGPL化され、Javaで書いたコード以外のライセンス汚染が問題になること を誰が想像しただろうか? もし、私が10年前の自分にいえるとすれば、 「今すぐモバイル開発にさらに時間を集中させるか、C++の制御技術やパターン認識の知識をもっと深めろ」 と言っているかもしれません。 ちなみに 当時の記憶ですが、2008年頃、学校で勉強していたころ、Javaベースのモバイルウィジェット開発のフレームワークを作った「Jig.jp」さんというところがあります。 2008年といえば、Java SE 6が生まれた頃だったと思います。 今でこそJavaベースのモバイルアプリ開発はごく日常的なことだと思いますが、 よーく考えたら、2008年の時点でJavaがモバイルウィジェットの概念の発想が出来るかもと注目した人は、なかなか素晴らしい着眼点を持っていたかもしれません。

Viewing all articles
Browse latest Browse all 9707

Trending Articles