SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

私がScalaを選んだ理由

JavaとRubyとScalaの比較

Scalaの能力

 では、RubyをはじめとしたLLが持っている動的な性質について、Scalaがどの程度実現できるか見てみましょう。

クラス図
クラス図

 このプログラムは、CarというクラスとEmployeeというクラスがname属性を共通して持っていますが、継承関係にありません。また、Carはprice、Employeeはsalaryを持っているので、それらの値をオブジェクトからcostというメッセージで取得できるように後付けでメソッド追加を行いたいと思います。

 クラスの定義だけならScalaの方では簡潔に書けるようです(内容によっては当然Rubyの方が楽なケースも多くあります)。

 RubyもScalaもクラスのオブジェクトを格納する変数に型を指定していません。Rubyは動的に処理されるため、変数には本当に型がありません。ところがscalaでは、コンパイラによって使われている型が推論されてつけられます。

 2つのクラスで同じメソッド(ここではname)を呼ぶためには、Javaなどではインターフェースや抽象クラスの使用が必要ですが、rubyでは変数に型がないためオブジェクトにメッセージを投げて、メソッドがあれば反応します(Duck typing)。

 一方Scalaでは、下記のように「nameメソッドを持ったクラス」のオブジェクトならprintNameメソッドを使うことができる、という文法になっています。これにより、Duck Typing風のことを実現しています。

 メソッドの追加に関してはRubyの方が数段柔軟です。実行時のオブジェクトに動的にメソッド追加なども朝飯前です。Scalaでは、逆にimplicit Conversion(暗黙の型変換)のテクニックを使います。下記の例でいくと、

implicit def priceToCost(in: {def price: Int}) = CostHolder(in.price)

 という記述をしておくと、costメソッドが必要な時だけCostHolderが動くイメージになります(つまりpriceメソッドの内容が返却される)という感じになります。

scala
class Car(var name:String,val price:Int);    // Carクラスの定義
class Employee(var name:String, var salary:Int);
                                             // Employeeクラスの定義
case class CostHolder(cost: Int) // costメソッドを持ったcaseクラス

 // priceメソッドをもったクラスにcostメソッド追加したように動かす
implicit def priceToCost(in: {def price: Int}) = CostHolder(in.price)

// salaryメソッドを持ったクラスにcostメソッド追加したように動かす
implicit def salaryToCost(in: {def salary: Int}) 
  = CostHolder(in.salary)

// nameメソッドを持ったクラスのnameメソッドを表示
def printName(x:{def name:String}) = println(x.name) 


var legacy = new Car("legacy", 3000000) // 型推論
var yamada = new Employee("yamada", 200000)

printName(legacy) // Duck Typing風
println("price:"+legacy.price) // 通常のメッセージ
println("cost:"+legacy.cost) // メソッドの後付け
printName(yamada)
println("salary:" + yamada.salary)
println("cost:" + yamada.cost)
ruby
class Car  # Carクラスの定義
  attr_accessor :name, :price
  def initialize(name, price)
    @name = name
    @price = price
  end
end
class Employee # Employeeクラスの定義
  attr_accessor :name, :salary
  def initialize(name, salary)
    @name = name
    @salary = salary
  end
end

class Car # Carクラスにcostメソッドを後付け
  def cost
    @price
  end
end

legacy = Car.new("legacy", 3000000)
yamada = Employee.new("yamada", 200000)

  #Employeeクラスのオブジェクトにcostメソッドを後付け
yamada.instance_eval "def cost\n @salary \nend" 

puts legacy.name # Duck Typing
puts legacy.price # 通常のメッセージ
puts legacy.cost  # メソッドの後付け
puts yamada.name
puts yamada.salary
puts yamada.cost # 実行時にメソッドを後付け

 実際にScalaをコーディングしてみると、「う~ん。なかなかええ感じやけど、さすがにRubyに比べると面倒やなぁ…」という感想でした。

 「これやったら、Rubyでできるんやから、Rubyから乗り換える必要ないなぁ」と正直思ったのですが、当時の自分は間違っていました。Scalaの比較対象はRubyではないのです!

次のページ
ScalaはJavaの後継のポジションに最適

この記事は参考になりましたか?

特集記事連載記事一覧

もっと読む

この記事の著者

牛尾 剛(ウシオ ツヨシ)

株式会社豆蔵 BS事業部 エンタープライズチーム。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/2464 2008/05/16 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー