JavaとRubyを個人的に比較してみた

面接で聞かれてしゅって答えられず痛い目にあったので自分用に整理としてみる。


# 概要
自分の経験期間は以下(2020年7月現在)

|言語|使用期間|使用用途|
|----|----|----|
|Python|2018.5 ~ 2020.3|(1)JupyterNotebook上でのWebスクレイピングや機械学習を含むデータ解析 (2)MLOpsにおけるAWS Lambdaの実行スクリプトやAWSBatchの実行ファイル|
|Java| 2018.8 ~ 2019.5|Javaによるスタンドアロンアプリ開発|
|Ruby| 2019.11 ~| RailsでのWebアプリ開発|


- オブジェクト指向やシステム開発の基礎を身に付けたのはJava、Webアプリを実際に作ったのはRuby on Railsである。
- PythonのDjangoはチュートリアルだけやった
- PYthonはデータ解析メインで使ったため、システム開発の経験があるJavaとRubyを比較する。



# Java
- ウェブフレームワークに加え、kafka, Hadoopなどデータ処理系に結構よく使われている印象。
- コンパイル型言語で処理速度速め
    - バイトコードコンパイル型言語
    - つまり、javacコマンドでコンパイルした時点では**マシン語ではなくバイトコード(中間コード)**になっている。
        - ソフトウェアはこの状態で配布されていて、JVMが入っていればどこでも動く。
    - JVM上でこのバイトコードをJustInTimeコンパイル(実行時コンパイル)してマシンコードにしながらCPUに実行される。
        - JITコンパイルとは、ソフトウェアを構成するモジュールやクラス、関数などの、ある単位のコードがまさに実行されるその時に、コンパイルすること。
- マルチスレッドが比較的よく使われる印象。[デザインパターン入門のマルチスレッド編](https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88-Java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80-%E3%83%9E%E3%83%AB%E3%83%81%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E7%B7%A8-%E7%B5%90%E5%9F%8E-%E6%B5%A9/dp/4797331623)出てるし。


# Ruby
- webフレームワーク(Rails)での使用が圧倒的に多い印象
- インタープリタ型言語で処理速度遅い(らしい)
- 書いてて楽。以下が要因としてあげられる。
    - メソッドの()が省略可能であること。個人的には気持ち悪いが...
    - クラスのメソッドにaliasが貼られていることが多いこと。([例](https://github.com/rails/rails/blob/b2eb1d1c55a59fee1e6c4cba7030d8ceb524267c/activesupport/lib/active_support/core_ext/numeric/bytes.rb#L17))
    - ブロックの導入。
        - これに関しては、読みやすさに全振りするために導入したものではないか？と感じる。メソッドの引数にブロックの代わりにラムダ関数を入れて渡すのでも問題ないように思えるし。
        - ブロックの導入、さらにブロックを`{|x|...}`のみならず`do |x| ... end`という書き方を許容したことで、以下のようにeach/map/filterと言った関数型プログラミングと合わせた時の書きやすさ/読みやすさが抜群に高まった。(自分が初めて以下を見た時どうしたらこのような書き方が許容される言語仕様になるのか本当に不思議だった)

```
[1, 2, 3].map do |each_num|
    each_num + 1
end
```

- 定数が書き換え可能
    - 定数はVarのように、大文字始まりで命名された変数
    - 定数への再代入も、定数の破壊的メソッドの実行も(警告が発生するが)できてしまう
        - freezeメソッドで破壊的メソッドは防止できる
- メタプログラミング(黒魔術)ができる。
    - コードを書く人は楽だが、コードを読む人は結構追跡が大変になるのでそんなに好きじゃない。
    - これは以下のメソッドによって実現されている。
        - `eval "@#{key}=value"`でStringオブジェクトをコードとして解釈できる
        - `define_method str {...}`でクラス内で動的にメソッドを追加できる
        - `send(:method_name, arguments)`で動的なメソッド呼び出しができる
- マルチスレッドプログラミング


# Java vs Ruby

- Javaのinterfaceを実装 <=> RubyのmoduleをMixIn
    - Javaのインターフェイスはメソッドの中身は実装していない
    - RubyのMixInに使われるモジュールは中身が実装してある
        - [モジュールのインスタンスメソッドからinclude元のクラスのインスタンス変数/メソッドを呼び出すことが可能である](https://qiita.com/kudojp/items/fb8d6e88e368e3181fa3)
        - モジュールを書く時点で大分include元のクラスのことを知っている必要がありそうだなあ、と予想される。
あんまりいいとは思えない。

- エラーハンドリングが、Javaではメソッド毎に必須 <=> Rubyだと必須でない
    - Javaでは、メソッド毎で必ず例外処理をする(か例外を明示的にthrowする)必要がある。
        - RuntimeExceptionとIllegalArgumentExceptionはその必要はない
    - Rubyでは、メソッド内でハンドリングされないエラーが生じた場合、その時点で現在のメソッドを終了して呼び出し元にエラーが引き継がれる。
        - ちなみに`Railsだと、アクティブレコードで生じたActiveRecord::RecordNotFoundがコントローラーのアクションで明示的にハンドリングされなかった場合、404エラーのHTTPレスポンスが返される`という仕様があることを知った時には、本当によくできているものだと感動した。
    - ちなみにGolangだと、エラーオブジェクトは返り値として返す。

- Javaではprimitive型が存在する <=> Rubyは全ての値はオブジェクト
    - Javaでは、`boolean, byte, char, short, int, float long, double`がprimitive型。
        - これらにはラッパークラスが存在している。例えばリストの要素としてintを含める場合には、`List<int>`ではなく、ラッパー型のjava.lang.Integerを用いて`List<Integer>`にしてやる。
        - primitive型を使うメリットは[ここ](http://itstudy365.com/blog/java-%E3%83%97%E3%83%AA%E3%83%9F%E3%83%86%E3%82%A3%E3%83%96%E5%9E%8B%E3%81%AE%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88%E3%81%A3%E3%81%A6/)に詳しい。
            - 知らなかった...
    - Rubyだとintegerもクラスであるため、`3.each{...}`のように数値からメソッドを生やすことができる。
        - これまたRubyは書いてて楽しい、との評判に貢献していそうではある。


- equalsと==の意味が逆なのがややこしくてストレシング。
    - Javaだと`==は同一性`、`.equals()は同等性`
        - `hashのキーの比較は.equals()`を用いる。([ここ](https://qiita.com/kudojp/items/2b70f3df4612f0881f16)で確認済)
        - equals()をoverrideしたらhashCode()もoverrideする必要がある。
        - `A.equals(B)がtrue`なのに`AとBのhashCode()が異なる`ことは許されない。
        - `A.equals(B)がfalse`なのに`AとBのhashCode()が同じ`になることは許される。ただしこの場合連鎖法で実装されたハッシュでは[衝突](https://programming-place.net/ppp/contents/algorithm/search/006.html#hash)が生じる。
    - Rubyだと`==は同等性`、`equal?()は同一性`
        - hashのキーの比較は`eql? = equal()? && 比較する二つのオブジェクトのクラスが同一か?`を用いる。
        - eql?()をoverrideしたらhash()もoverrideする必要がある。
        - `A.eql?(B)がtrue`なのに`AとBのhash()が異なる`ことは許されない。
        - `A.eql?(B)がfalse`なのに`AとBのhash()が同じ`になることは許される。ただしこの場合連鎖法で実装されたハッシュでは[衝突](https://programming-place.net/ppp/contents/algorithm/search/006.html#hash)が生じる。
        - `Object#==`は、デフォルトでは equal? と同じオブジェクトの同一性判定になっている。([ref](https://docs.ruby-lang.org/ja/latest/method/Object/i/=3d=3d.html))



# どういう時にどっちを使うべきか？

## Java
- 歴史あり、金融系や業務系に

## Ruby on Rails
- Pros
    - ライブラリが充実している
    - スタートアップが高速でアプリを作るのには最適
    - 日本人エンジニア多い
- Cons
    - ライブラリのバージョンアップ対応がコストかかる