JavaエンジニアのためのRuby入門

  • 11
    Like
  • 0
    Comment

ディップ Advent Calendar の17日目です:)

はじめに

どんな記事?

JavaエンジニアがRubyを書くようになり、
これJavaの◯◯みたいなことだったのか!」と思ったことを共有する記事です。

今回はコーディングについて書きます…!

目次

クラス
インスタンス化
メソッド
インスタンス化(引数あり)
配列/リスト
Map/ハッシュ
知っておくと役立ったこと
おわりに

クラス

Java

Father.java
// デフォルトパッケージ
public class Father {
}

Ruby

father.rb
class Father
end

インスタンス化

Java

Main.java
// デフォルトパッケージ
public class Main {

    public static void main(String[] args) {
        Father father = new Father(); 
    }

}

Ruby

main.rb
require './father.rb' # ./father.rbを読み込む。javaでいうimportに似ています。

father = Father.new

メソッド

Java

宣言

Father.java
public class Father {

    // staticなメソッド
    public static void hello() {
        System.out.println("Hello...");
    }

    // 非staticなメソッド(インスタンスメソッド)
    public void tellTheTruth() {
        System.out.println("I am your father.");
    }

    // 非staticなメソッド(インスタンスメソッド):引数あり
    public void fuga(String words) {
        System.out.println(words);
    }

}

呼び出し

Main.java
public class Main {

    public static void main(String[] args) {
        Father.hello(); // Hello...
        Father father = new Father(); 
        father.tellTheTruth(); // I am your father.
        father.fuga("Nooo!!"); // Nooo!!
    }

}

Ruby

宣言

father.rb
class Father
  # クラスメソッド
  def self.hello
    puts 'Hello...'
  end

  # インスタンスメソッド(引数なし)
  # snake_case記法
  def tell_the_truth
    puts 'I am your father.'
  end

  # インスタンスメソッド(引数あり)
  def fuga(words)
    puts words
  end
end

呼び出し

main.rb
require './father.rb'

Father.hello # hoge

father = Father.new

father.tell_the_truth() # I am your father.
father.tell_the_truth   # I am your father. ()は省略可能。メンバの呼び出しに見えますが、メソッドの呼び出しです。

father.fuga('nooooo!!') # nooooo!!
father.fuga 'nooooo!!'  # nooooo!! ()は省略可能。よく使用されますが、メソッドの右辺などでは分かりづらくなるので避けた方が良いでしょう。

インスタンス化(引数あり)

前述のインスタンス化では引数なしのコンストラクタでしたが、今回は引数を持つコンストラクタでよりオブジェクト感を出していきます。

Java

Father.java
public class Father {

    private String name;

    public Father(String name) {
        this.name = name;
    }

    public void tellTheTruth() {
        System.out.println("I am " + name + "...");
    }

}

Ruby

father.rb
class Father
  attr_accessor :name

  def initialize(name)
    @name = name
  end

  def tell_the_truth
    puts "I am #{name}..."
  end
end

initializeがいわゆるコンストラクタです。
また、@hogeはインスタンス変数を表します。
Javaではよく、フィールドとかメンバ変数とか呼ばれるものですね。

文字列について

  • 文字列はシングルクォーテーション'で囲みましょう。
    • ダブルクオーテーション"は式展開時に使用します。
    • 文字列はStringクラスのインスタンスです。

attr_accessor / アクセサについて

attr_accessorはアクセサ(いわゆるgetter/setter)を定義するものです。
実際は、以下のコードを記述しているのと同じです。

def name
  @name 
end

def name=(val)
  @name = val
end

Javaの以下と同じです。

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

配列/リスト

Java

Main.java
package com.company;

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("pen", "pineapple", "apple", "pen");

        // java5-7
        for (String word : words) {
            System.out.println(word);
        }
        // java8以降
        words.forEach(System.out::println);
    }
}

Ruby

ppap.rb
words = ['pen', 'pineapple', 'apple', 'pen']

puts words[0] # pen

words.each do |word|
  # 以下の処理をループ実行する。
  # wordにはループされたオブジェクトが格納されている。
  # よって、最初のループは'pen'がコンソールに表示される。
  puts word
end

配列はArrayクラスで表現されます。
Array - Rubyリファレンス

配列の繰り返しは#eachがしばしば使用されます。
for文もありますが、まずは#eachを覚えておけば大概のループは大丈夫でしょう。

doからendで表されるブロックについては、以下の記事が非常にわかりやすいです。

[Ruby] ブロックやProcの使いどころを理解する
(混乱する可能性があるので、ストックして後で読む方が良いかもしれません。)

また、文字列の配列は以下の書き方で生成されることも頭の片隅に置いておくと、
他の人が書いたコードを読んでドキドキする可能性が低くなるでしょう。

# 文字列の配列を生成する
words = %w(pen pineapple apple pen)

このあたりは、Rubyで%記法(パーセント記法)を使うが非常に参考になります。
(ストック推奨)

Map/ハッシュ

Java

Main.java
import java.util.HashMap;
import java.util.Map;

public class Main {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();

        map.put("name", "ディップ");
        map.put("service", "ナースではたらこ");

        System.out.println(map.get("name")); // ディップ
        System.out.println(map.get("service")); // ナースではたらこ
    }

}

Ruby

Rubyにおけるハッシュはいくつか書き方が用意されています。

main.rb
# 1 文字列をキーとする
dip = {'name' => 'ディップ', 'service' => 'ナースではたらこ'}

puts dip['name'] # ディップ
puts dip['service'] # ナースではたらこ

# 2 シンボルをキーとする。
dip = {name: 'ディップ', service: 'ナースではたらこ'}

puts dip[:name] # ディップ
puts dip[:service] # ナースではたらこ

書き方は上記だけではなく、また、バージョンによってもサポートが異なります。
以下の記事が非常にわかりやすいので目を通すと良いでしょう。
Rubyのhash宣言方法いろいろ

知っておくと役立ったこと

名前空間について(Foo::Bar.newについて)

以下の書き方を見て戸惑うことがあるかもしれません。

Foo::Bar.new

こちらは、実際には以下のクラスをインスタンス化している処理です。

module Foo
  class Bar
  end
end

moduleの役割の一つに名前空間の提供があります。(他にはメソッドの提供などの機能を持ちます。)
Javaでいうパッケージに近い概念ですね。

Bar.java
package foo;

public class Bar {
}
Fuga.java
package hoge;

import foo.Bar;

public class Fuga {
    Bar bar = new Bar();
}

上記と似ています。

NullPointerExceptionではなく、NoMethodErrorが発生する

RubyのnilはJavaでいうnullに似ていますが、多少異なります。

nil は NilClass クラスの唯一のインスタンスです。 nil は false オブジェクトとともに偽を表し、 その他の全てのオブジェクトは真です。
class NilClass (Ruby 2.3.0)

よって、nilクラスに存在しないメソッドを呼び出した際は、
NullPointerExceptionというような例外ではなく、NoMethodErrorという例外が発生します。

main.rb
# 定義していないメソッドを呼び出す。
nil.special_method 
# NilClassにはspecial_methodとうメソッドはないので、以下のエラーが出力される。
undefined method `special_method' for nil:NilClass (NoMethodError)

おわりに

Rubyは文法が美しい上、組み込みクラスが便利で、使えば使うほど手に馴染む言語だと個人的に思います。
Javaから入ると「型宣言がないと何か不安!!」みたいになるかもしれませんが、すぐに慣れると思います。(3日で慣れました。)
他言語を始めると元の言語への見方も変わるので、是非始めてみるのはいかがでしょうか。