2013年1月1日火曜日

Scalaでダックタイピング?implicitを使ってみたよ

あけましておめでとうございます。

年明け最初の記事は、Scalaの勉強ということでimplicitを使ってみた話。

Javaの実装でtoHoge()な書式が好きなので、interfaceを使って実現をするのですが、
既存のクラスに関してはそれができません。

その具体例と、対応方法は次のコード。
Outputableを実装したクラスを受け取って出力を行うクラスです。

public interface Outputalbe {
public String output();
}
view raw Outputable.java hosted with ❤ by GitHub


自前のクラスに関しては、implementsすれば好き勝手に実装ができますね。

public class Profile implements Outputalbe {
private final String name;
private final int age;
public Profile(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String output() {
return String.format("Hey, I am %s , and I'm %d old.", this.name,
this.age);
}
}
view raw Profile.java hosted with ❤ by GitHub


そして出力用のコード。

public class Display {
public static void main(String[] args) {
Display display = new Display();
Outputalbe data = new Profile("numa08", 24);
display.out(data); //Hey I am numa08 and , I'm 24 old.
}
private void out(Outputalbe data) {
System.out.println(data.output());
}
}
view raw Display1.java hosted with ❤ by GitHub


しかし、既存のクラスに関してはそうはいかないので、ラッパークラスを
用意することになります。

import java.text.SimpleDateFormat;
import java.util.Calendar;
public class OutputableCalendar implements Outputalbe {
private final Calendar calendar;
public OutputableCalendar(Calendar calendar) {
super();
this.calendar = calendar;
}
@Override
public String output() {
SimpleDateFormat format = new SimpleDateFormat("'Today is 'yyyy/MM/dd");
return format.format(this.calendar.getTime());
}
}


import java.util.Calendar;
import java.util.GregorianCalendar;
public class Display {
public static void main(String[] args) {
Display display = new Display();
Calendar today = GregorianCalendar.getInstance();
Outputalbe data = new OutputableCalendar(today);
display.out(data); //Today is 2013/01/01
}
private void out(Outputalbe data) {
System.out.println(data.output());
}
}
view raw Display2.java hosted with ❤ by GitHub


呼び出し元も、新しいクラスを呼び出したりで面倒くさい。

Javaという言語は面倒臭いですん。

さて、Rubyではもっと柔軟な感じにいけて、クラスの定義を
コードの途中で再開させることができるのです。

まずは、自前のクラスに対しての実装。
require "./Profile.rb"
def display(data)
puts data.output
end
profile = Profile.new("numa08", 24)
display(profile) #Hey I am numa08 and , I'm 24 old.
view raw Display1.rb hosted with ❤ by GitHub


class Profile
def initialize(name, age)
@name = name
@age = age
end
def output
"Hell, I am #{@name} , and I'm #{@age} old."
end
end
view raw Profile.rb hosted with ❤ by GitHub


そして、既存のクラスの拡張。
class Time
def output
strftime("Today is %Y/%m/%d")
end
end
view raw Time.rb hosted with ❤ by GitHub


def display(data)
puts data.output
end
today = Time.now
display(today) #Today is 2013/01/01
view raw Display2.rb hosted with ❤ by GitHub


Rubyって便利。

そして、最後にScala。暗黙的変換のimplicitキーワードを使ったメソッドを
用意することで、コンパイラが自動的に適した型に変換するメソッドを呼び出して
くれるのです。

trait Outputable {
def output:String
}


class Profile(name:String, age:Int) extends Outputable{
override def output = {
"Hey I am " + name + " and , I'm " + age + " old."
}
}
view raw Profile.scala hosted with ❤ by GitHub


def output(data:Outputable) = {
println(data.output)
}
val me = new Profile("numa08", 24)
output(me) //Hey I am numa08 and , I'm 24 old.
view raw Display1.scala hosted with ❤ by GitHub


implicitを使って変換を行う例。

import java.text.SimpleDateFormat;
import java.util.Calendar;
class OutputableCalendar(calendar:Calendar) extends Outputable{
override def output = {
val format = new SimpleDateFormat("'Today is 'yyyy/MM/dd")
format.format(calendar.getTime)
}
}
implicit def toOutput(calendar:Calendar) = new OutputableCalendar(calendar)


import java.text.SimpleDateFormat;
import java.util.Calendar;
def output(data:Outputable) = {
println(data.output)
}
val today = Calendar.getInstance
output(today) //Today is 2013/01/01
view raw Display2.scala hosted with ❤ by GitHub


実に気持ちがいい。呼び出し元は、クラスの変換とか意識しなくていいんですよ。

ただ、Javaでも型変換にtoHoge()な実装をしない
コードをたくさん見てきたので、ちょっとアレ。

今年も、綺麗なコードを求めてがんばりますね。

さて、今回参考にした書籍は次。


あと、最近はPlay frameworkを使ってScalaを
勉強してます。