LoginSignup
7
5

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

Last updated at Posted at 2020-07-05

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

概要

自分の経験期間は以下(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コンパイルとは、ソフトウェアを構成するモジュールやクラス、関数などの、ある単位のコードがまさに実行されるその時に、コンパイルすること。
  • マルチスレッドが比較的よく使われる印象。デザインパターン入門のマルチスレッド編出てるし。

Ruby

  • webフレームワーク(Rails)での使用が圧倒的に多い印象
  • インタープリタ型言語で処理速度遅い(らしい)
  • 書いてて楽。以下が要因としてあげられる。
    • メソッドの()が省略可能であること。個人的には気持ち悪いが...
    • クラスのメソッドにaliasが貼られていることが多いこと。()
    • ブロックの導入。
      • これに関しては、読みやすさに全振りするために導入したものではないか?と感じる。メソッドの引数にブロックの代わりにラムダ関数を入れて渡すのでも問題ないように思えるし。
      • ブロックの導入、さらにブロックを{|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だと必須でない

    • 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型を使うメリットはここに詳しい。
        • 知らなかった...
    • Rubyだとintegerもクラスであるため、3.each{...}のように数値からメソッドを生やすことができる。
      • これまたRubyは書いてて楽しい、との評判に貢献していそうではある。
  • equalsと==の意味が逆なのがややこしくてストレシング。

    • Javaだと==は同一性.equals()は同等性
      • hashのキーの比較は.equals()を用いる。(ここで確認済)
      • equals()をoverrideしたらhashCode()もoverrideする必要がある。
      • A.equals(B)がtrueなのにAとBのhashCode()が異なることは許されない。
      • A.equals(B)がfalseなのにAとBのhashCode()が同じになることは許される。ただしこの場合連鎖法で実装されたハッシュでは衝突が生じる。
    • Rubyだと==は同等性equal?()は同一性
      • hashのキーの比較はeql? = equal()? && 比較する二つのオブジェクトのクラスが同一か?を用いる。
      • eql?()をoverrideしたらhash()もoverrideする必要がある。
      • A.eql?(B)がtrueなのにAとBのhash()が異なることは許されない。
      • A.eql?(B)がfalseなのにAとBのhash()が同じになることは許される。ただしこの場合連鎖法で実装されたハッシュでは衝突が生じる。
      • Object#==は、デフォルトでは equal? と同じオブジェクトの同一性判定になっている。(ref)

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

Java

  • 歴史あり、金融系や業務系に

Ruby on Rails

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5