インタプリタとコンパイラ
JavaはJITコンパイラ言語である。細かく言えば違うがRubyとの対比で考えるならそういう認識でおおよそ問題ない。コンパイラ系であるため実行速度はRubyに比較して圧倒的に速いが、毎度コンパイルしないと実行できない不便さはある。
対してRubyはインタプリタ言語である。コンパイラを通さずに直接実行できるが、毎回コードを評価する必要があるため実行速度ではJavaに劣る。その分、簡単なコードを手軽に実行できる身軽さがある。
堅牢性と自由さ
Javaは非常に厳密で堅牢性の高いコードを書ける言語である。コンパイラという検問所が徹底的にコードを走査するため少なくとも文法上誤ったコードを書くことは許されない。そのため結果としてオブジェクト構造の視認性は極めて高い。
Rubyは自由で簡潔なコードを書ける言語である。そこには堅牢さのかけらも無いが、だからこその自由がある。そして自由とは自己責任である。Javaのように怒ってくれるお母さんはいないので、プログラマは自分だけの力で安全なコードを書かなくてはならない。
喩えるならJavaはグランツーリスモやForza Horizonなどのゲームでレースをするようなものだ。どんなに酷い運転をしてもあくまでゲーム、画面の中の車がひしゃげてもプレイヤーには何の影響も無い。対してRubyは生身の人間がハチロクで峠を攻めるようなものだ。レースゲームのようなイカれた速度は出せないがその体感がダイレクトに自分に返ってくる楽しさがある。しかしハンドル操作を誤れば大怪我ではすまない。
言語適正
Javaはその堅牢性ゆえにある程度レベルの低いメンバーでもそれなりに安全なコードを書けるのでチーム開発に向いている。そして一見難しそうな見た目とは裏腹にむしろ初心者向けの言語である。怖い監督がいつも隣で目を光らせているからだ。
Rubyはその自由さゆえにある程度レベルの高いメンバーでなければ安全なコードを書くことができない。隣がハンドル操作を誤ればチームメンバーも一緒に崖下である。ゆえに個人開発に向いている。そして一見簡単そうな見た目とは裏腹にむしろ上級者向けの言語でもある。
| 比較対象 | Java | Ruby |
|---|---|---|
| 速度 | 比較的速い | 比較的遅い |
| コンパイルの手間 | ある | ない |
| 堅牢性 | 高い | 低い(無い) |
| 開発現場 | チーム開発向け | 個人開発向け |
| 対象レベル | 初心者向け | 上級者向け |
つまり理想を言えば、Javaでオブジェクト指向と堅牢なアプリケーションの基礎を学び、チーム開発を経て可読性やドキュメントの重要性を学び、ある程度プログラミングに習熟したプログラマがコンパイラや開発チームという枷から解き放たれて、Rubyという自由な世界で自分の力を十全に発揮してその混沌を切り開いていく、というのが最適解と言える。
Java経験者がRubyのために最初に覚えるべき事
Rubyは全てがオブジェクト
- つまりリテラルもオブジェクトである。
true、falseもそれぞれのクラスのインスタンスとして実装されている。 -
nullに該当するのはnilである。nilもNilClassのインスタンスである。つまりnullと異なり実体がある。 - これはつまりRubyにはプリミティブ型が存在しない事を意味する。
1やnilからメソッドを呼び出す事も可能である。
Rubyでの真偽値
- Rubyでは
falseとnilのみがfalse、それ以外は全て(0も""も[]もあらゆるオブジェクトが)trueである。 - Rubyにはboolean型やBooleanクラスが存在しない。ある意味で全てのオブジェクトがbooleanとして扱えると言える。これはRubyの強力な特徴である。
Rubyのクラス名は定数
- 定数は値を持つ。つまりクラス名定数も値を持つ。
- クラス名定数の値はClassオブジェクト(つまりClassクラスのインスタンス)である。
Rubyのself
-
selfはJavaのthisに該当する。thisと異なりクラス定義内(Javaでいうstatic初期化ブロック)を含め、あらゆる場所で使用できる。
Rubyのインスタンス変数、クラス変数
- Rubyのインスタンス変数はJavaでいうインスタンスフィールドに該当する。クラス変数はstaticフィールドに該当する。
- Javaと異なり変数のスコープは変更できない。Javaの
privateとprotectedの中間ぐらいで固定。 -
publicにアクセスする事はできないので、アクセサなどでゲッターやセッターを定義する必要がある。
Rubyは型宣言が不要
- 異なる型での上書きも可能。
- メソッドのシグネチャも型情報が含まれないためメソッドの判別ができない。つまりオーバーロード機構が存在しない。
Rubyの演算子のほとんどはメソッドとして実装されている
- つまり演算子の左辺と右辺で明確に意味が異なる。
- 演算子メソッドはほとんどの場合
left_side_obj.operator_method(right_side_obj)として評価される。例外的に!メソッド はオブジェクトの前に置くことを許される。逆に言えば!演算子 は通常のメソッド呼び出しとしても書ける。(true.! == !true == false) - 演算子メソッドを再定義することができるため、演算子の結果を変更する事ができる。
Rubyのオブジェクトは複数のインスタンスを持つ
-
self(自身のオブジェクト)とself.singleton_class(特異クラスオブジェクト)の2つのインスタンスを持ち、self.class(Classオブジェクト)への参照を持つ。-
selfインスタンスがsingleton_classとclassの二つのクラス参照を持つと言い換えても良い。どちらでも覚えやすい方で覚えて。
-
- Classオブジェクトもオブジェクトなので当然、
selfとsingleton_classとclassへの参照を持つ。 - Javaではオブジェクトは、インスタンスとクラス情報(型)という組み合わせだが、Rubyでは全く異なる。
Rubyにはパッケージは存在しない
- モジュールやクラスの名前空間をパッケージのように使うことができる。
- モジュールの内部にクラス定義することで、
ModuleName::InnerClassNameの形で名前空間に所属できる。
Rubyにはインターフェースが無い
- モジュールがインターフェースのようにも使える。
- モジュールを
includeするとJavaのインターフェースのように継承関係を構築する。 - abstractメソッドは定義できない。モジュールのインスタンスメソッドがインターフェースのデフォルトメソッドのように振る舞う。
Rubyにstaticメソッドは存在しない
- 特異メソッドがその代替として使われる。
- Rubyでクラスから直接メソッドを呼ぼうとした場合、クラスオブジェクトはClassクラスのインスタンスであるためClassクラスのインスタンスメソッドが探索されてしまいクラスに定義したインスタンスメソッドは探索されない。クラスに特異メソッドを定義すると、Classクラスのインスタンスメソッドの前に特異メソッドが探索されるので、Javaのstaticメソッドのように振る舞う事ができる。
- staticメソッドを集めたstaticライブラリのような機能は、モジュールに特異メソッドを集める事で実現できる。
-
static importのようにも使いたい場合は特異メソッドをモジュール関数として定義し、includeすることで実現できる。
Rubyのイテレータ
- Rubyは非常に強力な内部イテレータと完全なクロージャであるブロックを併用できるため、Javaの拡張for文のような外部イテレータはほとんど使われない。
- そもそもイテレータが強力すぎてループ文自体がほとんど使われない。
- Javaプログラマの観点ではイテレータは比較的使いづらい仕様であったが、Rubyのイテレータはブロック内で直接コードを書けるので直感的にわかりやすく非常に使いやすい。
Rubyのコレクション
- Rubyのコレクションは実質的にArray、Hashの二種類しか存在しない。後期バージョンや拡張ライブラリではSetも使用可能だがSetの中身はキーのみのHashだ。
- RubyのArrayはJavaのArrayList、HashはJavaのLinkedHashMapに相当する。
- 実用レベルでは2種類で全く問題ない。逆に使い分けに悩む必要も無いので楽といえば楽。
Rubyのcase文はJavaのswitch文よりも高機能
-
case文の比較はwhenに与えられたオブジェクトの===メソッドで行われる。オブジェクトごとにその振る舞いを変更できる。 - 例えば
case vの時、when 1ならv == 1の場合に実行されるが、when Integerならv.is_a?(Integer)の場合に実行される。 - 評価式(
v)を省略した場合、when v.odd?のようにwhenに判定文を書けるため複雑な制御構造にも対応可能。 -
caseは最後に評価した値を返すため、評価結果で代入する値を変えるような式はシンプルに書ける。
Rubyのメタプログラミング
- Javaのリフレクションよりも強力かつ柔軟なメタプログラミングが可能。
- 逆に言えばそれだけ安全性が犠牲になっているので、Javaの堅牢性が好きな人には発狂モノかもしれない。そういうモンだと諦めて。
Rubyはスコープを制御できない
- Javaの
privateやprotectedに相当するアクセス制御は存在しない。 - Rubyで定義できる
private、protectedは呼び出し制限(call limit)であり全くの別物。むしろアノテーションに近い。 - とりあえずRubyには完全に
privateな変数やメソッドは存在しない事だけ覚悟しといた方が良い。そういうモンだと諦めて。
RubyのfreezeとJavaのfinalは同じではない
- Rubyの
freezeはオブジェクトの変更を禁止する。例えば配列の内容は変更できなくなるが、変数の上書き(再代入)は可能。 - Javaの
finalは変数の再代入(上書き)を禁止する。例えば変数の中身の配列は変更可能。 - 上記の通り、Rubyの
freezeとJavaのfinalはむしろ真逆の性能をしている。 - Javaで変更不可能なコレクションを定義する事はできるが、既存のコレクションを変更不可にする事はできない。
- Rubyでは定数も再代入可能(つまりクラス名定数も再代入可能)である。警告こそ出るが代入自体は防がれない。そういうモンだと諦めて。
一番重要な点
- 厳密で堅牢なコードを書けるJavaと異なり、Rubyは自由で簡潔なコードを書ける言語である。
- 設計思想が異なるため、RubyにJavaのような堅牢性のあるコードを求めるのは間違いだとはっきり言わせて欲しい。
- Rubyに堅牢性求めるのはキーパーにキャッチャーのプロテクターをつけさせるようなもので、そんなアホな事をするよりもRubyの自由さを楽しむ方が健全である。
まとめ
警告
Javaは初心者向け!
警告!!
Rubyは上級者向け!