目次


多忙な Java 開発者のための Scala ガイド

実装継承

Scala の継承でオブジェクトと関数が出会う

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: 多忙な Java 開発者のための Scala ガイド

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:多忙な Java 開発者のための Scala ガイド

このシリーズの続きに乞うご期待。

オブジェクト指向言語の設計においては、この 20 年の大半にわたる中心的な話題は、継承の概念でした。継承をサポートしない言語、例えば Visual Basic などは、実際の作業には不適切な「おもちゃのような言語」であるとして、あざけりの対象となっています。一方、継承をサポートする言語での継承のサポート方法はそれぞれ非常に異なるため、何時間にもわたる議論の元になりがちです。多重継承は (C++ の作成者が判断したように) 本当に必要なのでしょうか。それとも (C# や Java 言語の作成者が判断したように) 不要で醜悪なものなのでしょうか。Ruby と Scala は多重継承に関して中間的な方法を採用している新しい言語ですが、これについては前回の記事で Scala の trait を紹介した際に説明しました (「参考文献」を参照)。

クールな言語がどれもそうであるように、Scala も実装継承をサポートしています (「参考文献」を参照)。Java 言語では、単一実装継承モデルを使うと、基底クラスを拡張したり、新しいメソッドやフィールドを追加したりといった、さまざまなことができます。多少構文上の違いはありますが、Scala の実装継承は見た目も使い勝手も Java 言語の場合とほとんど同じです。両者の違いは、Scala でオブジェクト型の言語設計と関数型の言語設計が融合されていることにより生じています。ここには詳しく調べる価値のある内容が含まれているため、今回の記事ではこれについて説明します。

Plain Old Scala Object

このシリーズのこれまでの記事と同じように、Scala の継承システムを説明するための出発点にもPerson クラスを使います。リスト 1 は Person クラスの定義を示しています。

リスト 1. 私は Person です
        // This is Scala
        class Person(val firstName:String, val lastName:String, val age:Int)
        {
        def toString = "[Person: firstName="+firstName+" lastName="+lastName+
        " age="+age+"]"
        }

Person は非常に単純な POSO (Plain Old Scala Object) であり、読み取り専用のフィールドを 3 つ持っています。これらのフィールドを読み書き可能にするためには基本コンストラクターの宣言の中の val (value) を var (variable) に変えるだけでよい、ということを思い出してください。

いずれにせよ、Person 型の使い方も非常に簡単です (リスト 2)。

リスト 2. PersonApp
        // This is Scala
        object PersonApp
        {
        def main(args : Array[String]) : Unit =
        {
        val bindi = new Person("Tabinda", "Khan", 38)
        System.out.println(bindi)
        }
        }

このコードはまったく驚くようなものではありませんが、出発点としては十分です。

Scala での抽象メソッド

このシステムを発展させていくと、Person クラスには Person (人間) であるために必要な非常に重要なもの、つまり何かをする、という要素が欠けていることが明らかになります。私達の多くは、単に存在して場所を占有するだけではなく、自分が生活の中で何をするかによって自分自身を定義します。そこで、Person に何らかの目的を与える新しいメソッドを追加します (リスト 3)。

リスト 3. では、何かをしましょう (doSomething)
        // This is Scala
        class Person(val firstName:String, val lastName:String, val age:Int)
        {
        override def toString = "[Person: firstName="+firstName+" lastName="+lastName+
        " age="+age+"]"
        
        def doSomething = // uh.... what?
        }

これによって問題が生じます。Person は具体的に何をするのでしょう。絵を描く Person もいれば、歌を歌う Person や、コードを作成する Person、ビデオ・ゲームで遊ぶ Person、そしてまったく何もしない Person (10 代の若者の親に聞いてみてください) もいます。そこで、そうした活動を Person そのものの中に直接入れるのではなく、Person のサブクラスを作成して、そこに入れる必要があります (リスト 4)。

リスト 4. この Person は、ほとんど何もしません
        // This is Scala
        class Person(val firstName:String, val lastName:String, val age:Int)
        {
        override def toString = "[Person: firstName="+firstName+" lastName="+lastName+
        " age="+age+"]"
        
        def doSomething = // uh.... what?
        }
        
        class Student(firstName:String, lastName:String, age:Int)
        extends Person(firstName, lastName, age)
        {
        def doSomething =
        {
        System.out.println("I'm studying hard, Ma, I swear! (Pass the beer, guys!)")
        }
        }

このコードをコンパイルしようとすると、コンパイルできないことがわかります。これは、Person.doSomething メソッドの定義がまだ有効ではないためです。このメソッドは、(例えば例外をスローし、派生クラスの中でオーバーライドされる必要があることを示すなどの) 完全な本体を必要とするか、あるいは (Java コードでの抽象メソッドの場合と同じように) 本体を持つ必要がないかのどちらかです。リスト 5 では抽象的な方法を試しています。

リスト 5. 抽象クラスとしての Person
        // This is Scala
        abstract class Person(val firstName:String, val lastName:String, val age:Int)
        {
        override def toString = "[Person: firstName="+firstName+" lastName="+lastName+
        " age="+age+"]"
        
        def doSomething; // note the semicolon, which is still optional
        // but stylistically I like having it here
        }
        
        class Student(firstName:String, lastName:String, age:Int)
        extends Person(firstName, lastName, age)
        {
        def doSomething =
        {
        System.out.println("I'm studying hard, Ma, I swear! (Pass the beer, guys!)")
        }
        }

ここで、Person クラスが abstractキーワードによってどのように修飾されているかに注意してください。abstractはコンパイラーに対して、「そのとおり。このクラスは抽象クラスになっています。」ということを示唆しています。この点では、Scala は Java 言語と何も変わりません。

オブジェクトが関数と出会う

Scala ではオブジェクト言語のスタイルと関数型言語のスタイルが融合されているため、(上記のように) サブタイプを作成せずに実際に Person をモデル化することができました。これには少し戸惑うかもしれませんが、これは実際、Scala では 2 つの設計スタイルが統合されていること、そしてそこから非常に興味深い概念が生まれていることを裏付けているのです。

これまでの記事で、Scala が、Scala 言語の中の任意の値 ( IntFloatDouble など) を扱う場合とまったく同じように、関数を値として扱うと説明したことを思い出してください。ここではそれを活用し、Person は派生クラスの中でオーバーライドされるメソッドとしての doSomething を持つのではなく、(呼び出され、置換され、拡張される) 関数値としての doSomething を持つものとしてモデル化しています。

リスト 6. 働き者の Person
        // This is Scala    
        class Person(val firstName:String, val lastName:String, val age:Int)
        {
        var doSomething : (Person) => Unit = 
        (p:Person) => System.out.println("I'm " + p + " and I don't do anything yet!");
        
        def work() =
        doSomething(this)
        
        override def toString = "[Person: firstName="+firstName+" lastName="+lastName+
        " age="+age+"]"
        }
        
        object App
        {
        def main(args : Array[String]) =
        {
        val bindi = new Person("Tabinda", "Khan", 38)
        System.out.println(bindi)
        
        bindi.work()
        
        bindi.doSomething =
        (p:Person) => System.out.println("I edit textbooks")
        
        bindi.work()
        
        bindi.doSomething =
        (p:Person) => System.out.println("I write HTML books")
        
        bindi.work()
        }
        }

関数をファーストクラスのモデリング・ツールとして使う考え方は、Ruby や Groovy、そして ECMAScript (別名 JavaScript) などの動的言語や多くの関数型言語では非常に一般的な手法です。これは他の言語でも可能です (C++ ではポインターから関数へ、そして/あるいはポインターからメンバーそして関数へという方式で、また Java コードではインターフェース参照を匿名内部クラスで実装することで) が、それらの言語では Scala の場合に比べ (また Ruby や Groovy、ECMAScript 等に比べ)、はるかに大きな手間がかかります。これは関数型言語のプログラマーがよく話題にする「高階関数」の概念の拡張です。(高階関数については「参考文献」を参照してください。)

Scala では関数が値と見なされるため、実行時に機能を切り替える必要がありそうな任意の場所で関数値を利用することができます。この手法は、GoF (Gang of Four Strategy) パターンのバリエーションである、ロール・パターンと見なすことができます (GoF パターンでは、オブジェクトのロール (例えば Person の現在の雇用形態) を静的な型の階層での値として表現するよりも、実行時の値として表現した方が適切です)。

階層構造をさかのぼるコンストラクター

皆さんが Java コードを作成していた頃をちょっと思い出してください。場合によると、派生クラスが (基底クラスのフィールドを初期化するために) 自分のコンストラクターから逆に基底クラスのコンストラクターにパラメーターを渡さなければならない場合があります。Scala では、基本コンストラクターはクラスの「いわゆる」メンバーとして現れるわけではなく、クラス宣言に現れるため、基底クラスに向かって逆にパラメーターを渡すためにはまったく新しい方式をとります。

Scala では、基本コンストラクターのパラメーターは class 行に渡されますが、これらのパラメーターに対して val 修飾子を使用して、クラスそのものに対してアクセサー (そして var の場合にはミューテーターも) を容易に導入できるようにすることもできます。

そうすると、Scala の Person クラス (リスト 5) はリスト 7 の Java クラスになります (これは javap を実行した結果を表示しています)。

リスト 7. 人間が読めるようにしてください
        // This is javap
        C:\Projects\scala-inheritance\code>javap -classpath classes Person
        Compiled from "person.scala"
        public abstract class Person extends java.lang.Object implements scala.ScalaObje
        ct{
        public Person(java.lang.String, java.lang.String, int);
        public java.lang.String toString();
        public abstract void doSomething();
        public int age();
        public java.lang.String lastName();
        public java.lang.String firstName();
        public int $tag();
        }

JVM の基本的なルールは相変わらず有効です。つまり Person の派生クラスが作成されると、言語が何を主張していようとも、基底クラスに対して何かを渡す必要があります。(実際には、完全にこのとおりではありませんが、言語がこのルールを無視しようとすると JVM は少し機嫌が悪くなります。そのため、ほとんどの場合は何らかの方法でこのルールを守る必要があります。) もちろん Scala はこのルールを守る必要がありますが、それは JVM の機嫌が悪くならないようにするためだけではなく、Java の基底クラスの機嫌も良くしておくためでもあります。これはつまり、Scala では、基底クラスに対してアクセサーとミューテーターを導入できる構文を保持したまま派生クラスが基底クラスを呼び出せるような構文を何らかの方法により実現する必要がある、ということを意味します。

これをもっと具体的な形にするために、リスト 5 から Student クラスを作成したとします。そうすると次のようになります。

リスト 8. 悪い学生
        // This is Scala
        // This WILL NOT compile
        class Student(val firstName:String, val lastName:String, val age:Int)
        extends Person(firstName, lastName, age)
        {
        def doSomething =
        {
        System.out.println("I'm studying hard, Ma, I swear! (Pass the beer, guys!)")
        }
        }

この場合、コンパイラーは長々と深刻なエラーを出力します。なぜなら新しい一連のメソッド (firstNamelastNameage) を Student クラスに導入しようとしたからです。これらのメソッドは同じような名前の Person のメソッドと衝突する可能性があり、ここでは基底クラスのメソッドをオーバーライドしようとしているのか (そうだとすれば、これらの基底クラスのメソッドの背後に実装とフィールドが隠されているはずなので、オーバーライドするのは不適切です)、あるいは同じ名前の新しいメソッドを導入しようとしているのか (この場合も、これらの基底クラスのメソッドの背後に実装とフィールドが隠されているはずなので、新しいメソッドの導入は不適切です)、コンパイラーには必ずしもわかりません。この後すぐに基底クラスから適切にメソッドをオーバーライドする方法を説明しますが、今ここではそれについては問題にしません。

また、Scala では Person のコンストラクターに渡されるパラメーターは Studentに渡されるパラメーターと完全に 1 対 1 に対応する必要がない、という点にも注意する必要があります。この場合のルールは Java のコンストラクターのルールとまったく同じです。この記事では単に読みやすくするために 1 対 1 に対応させています。また、Student は (Java 言語の場合と同じように) コンストラクターのパラメーターをさらに要求することができます (リスト 9)。

リスト 9. 要求の多い生徒
        // This is Scala
        class Student(firstName:String, lastName:String, age:Int, val subject:String)
        extends Person(firstName, lastName, age)
        {
        def doSomething =
        {
        System.out.println("I'm studying hard, Ma, I swear! (Pass the beer, guys!)")
        }
        }

この場合もまた、少なくとも継承とクラスの関係に関する限り、Scala のコードと Java のコードがいかに似ているかがわかります。

構文の違い

ここまで見た限りでは構文が非常に似ていることを不思議に思っている人がいるかもしれませんが、結局のところ Scala では Java 言語の場合とは異なり、フィールドとメソッドを区別しません。これは実は意図的な設計判断であり、このおかげで Scala プログラマーは、基底クラスを使う人にフィールドとメソッドの違いがわからないようにするのが非常に簡単になります。リスト 10 を考えてみてください。

リスト 10. 私は何でしょう
        // This is Scala
        abstract class Person(val firstName:String, val lastName:String, val age:Int)
        {
        def doSomething
        
        def weight : Int
        
        override def toString = "[Person: firstName="+firstName+" lastName="+lastName+
        " age="+age+"]"
        }
        
        class Student(firstName:String, lastName:String, age:Int, val subject:String)
        extends Person(firstName, lastName, age)
        {
        def weight : Int =
        age // students are notoriously skinny
        
        def doSomething =
        {
        System.out.println("I'm studying hard, Ma, I swear! (Pass the beer, guys!)")
        }
        }
        
        class Employee(firstName:String, lastName:String, age:Int)
        extends Person(firstName, lastName, age)
        {
        val weight : Int = age * 4 // Employees are not skinny at all
        
        def doSomething =
        {
        System.out.println("I'm working hard, hon, I swear! (Pass the beer, guys!)")
        }
        }

weightがパラメーターを取らずに Int を返すように定義されている様子に注目してください。これは「パラメーターのないメソッド」で、Java 言語での「プロパティー」メソッドの様子と非常によく似ています。Scala では実際に weightを (Student の場合のように) メソッドとして定義することもでき、あるいは (Employee の場合のように) フィールドまたはアクセサーとして定義することもできます。構文がこのように決められているため、抽象クラスから派生するものの実装がある程度柔軟になります。Java でこれと同じ柔軟性を得られるのは、たとえ同じクラスの中からアクセスされる場合であっても、すべてのフィールドがそのフィールドの get/set メソッドによってアクセスされる場合のみであることに注意してください。この方法が適切か不適切かは別にして、このようにコードを作成する Java プログラマーは多くはないため、Java ではこの柔軟性が活用される機会は稀です。一方 Scala の方法は、隠しメンバーや private メンバーに対しても、public メンバーの場合とまったく同じように手軽に使うことができます。

@Override から override へ

派生クラスが自分の基底クラスの 1 つで定義されているメソッドの振る舞いを変更しなければならない、ということがよくあります。Java コードでこれを処理する場合には、単純に同じ名前とシグニチャーを持つ新しいメソッドをその派生クラスに追加します。この方法の欠点は、シグニチャーのタイプミスやわずかな曖昧さによって気付かないうちにエラーが発生し、コードはコンパイルできても実行時に「誤った処理がされる」可能性があることです。

その問題に対応するために、Java 5 のコンパイラーでは @Override アノテーションが導入されています。@Override は javac に対して、派生クラスで導入されたメソッドが実際に基底クラスのメソッドをオーバーライドしたかどうかを検証します。Scala では override は言語の一部になっており、このことを忘れるとコンパイル・エラーが生成されます。従って toString() 派生メソッドはリスト 11 のようになるはずです。

リスト 11. これは派生メソッドです
        // This is Scala
        class Student(firstName:String, lastName:String, age:Int, val subject:String)
        extends Person(firstName, lastName, age)
        {
        def weight : Int =
        age // students are notoriously skinny
        
        def doSomething =
        {
        System.out.println("I'm studying hard, Ma, I swear! (Pass the beer, guys!)")
        }
        
        override def toString = "[Student: firstName="+firstName+
        " lastName="+lastName+" age="+age+
        " subject="+subject+"]"
        }

ご覧のとおり非常に単純です。

final を使う

前のセクションでは派生クラスによるオーバーライドの説明をしましたが、派生物によるオーバーライドを許可することの反対は、当然、派生物のオーバーライドを禁止することです。基底クラスでは、子クラスがその子クラスの基底クラスの振る舞いを変更できないようにしたい場合や、子クラスがどんな派生クラスも決して持たないようにしたい場合もあります。Java 言語の場合、そうするためにはメソッドに final という修飾子を適用し、確実にそのメソッドがオーバーライドされないようにします。あるいは、そのクラス全体に final を適用することで派生が行われるのを防ぎます。Scala でも実装の階層の仕組みは同じです。メソッドに final を適用して子クラスがそのメソッドをオーバーライドできないようにするか、あるいはクラス宣言そのものに final を適用して派生を禁止します。

abstractfinaloverride に関するこの議論がすべて、一般的な名前を持つメソッドに適用されるだけではなく、「奇妙な名前を持つメソッド」(Java や C#、C++ のプログラマーが演算子と呼ぶもの) にも同じように適用されることに注意してください。そのため、サポートする必要のある抽象メンバー関数 (「+」、「-」、「*」、「/」や他の数学演算 (pow や abs など)) を定義する数学的な機能を想定した基底クラスまたは trait (例えば「Mathable」と呼ぶことにします) を定義するのは一般的なことです。すると他のプログラマーは、Scala に始めから組み込まれている他の算術型のように見える追加の型 (例えば Matrix クラス) を作成することができ、その型は「Mathable」を実装あるいは継承し、これらのメンバーを定義することができるのです。

違いはどこに

これまで見てきたように、Scala と Java の継承モデルがそれほど簡単に対応するなら、Scala のクラスが Java 言語から継承する、あるいはその逆も可能なはずです。実際、これは絶対に可能でなければなりません。なぜなら Scala は、Java のバイトコードにコンパイルされるすべての言語と同様、java.lang.Object から継承されたオブジェクトを生成する必要があるからです。注意点として、Scala のクラスは他のもの (trait など) からも継承する可能性があり、そのため実際の継承の解決やコード生成の動作は異なる可能性がありますが、最終的には何らかの形で Java の基底クラスから継承できる必要があります。(trait は振る舞いを持つインターフェースのようなものであり、Scala のコンパイラーはこれを実現するために trait をインターフェースに分割し、trait がコンパイルされた結果作成されるクラスの中でそのインターフェースを実装していることを思い出してください。)

しかし実は、Scala での型の階層は Java 言語の場合とは微妙に異なります。技術的には、Scala のすべてのクラスの継承元となる基底クラス (IntFloatDouble などの型やその他の数値型) は scala.Any 型です。scala.Any 型は Scala の任意の型で利用できる中核的なメソッド・セット (==!=equalshashCodetoStringisInstanceOfasInstanceOf) を定義しますが、これらのメソッドの大部分は名前から十分容易に理解することができます。scala.Any 型から Scala は 2 つの大きな枝に分かれ、「基本型」は scala.AnyVal から継承し、「クラス型」は scala.AnyRef から継承します。 (そして scala.ScalaObject scala.AnyRef から継承します。)

通常、これは直接気にしなければならない事項ではありませんが、Scala と Java という 2 つの言語にまたがっての継承を考慮する場合には、稀に興味深い副作用が起こる可能性があります。例えば、リスト 12 の ScalaJavaPerson について考えてみてください。

リスト 12. これは混成です
// This is Scala
        class ScalaJavaPerson(firstName:String, lastName:String, age:Int)
        extends JavaPerson(firstName, lastName, age)
        {
        val weight : Int = age * 2 // Who knows what Scala/Java people weigh?
        
        override def toString = "[SJPerson: firstName="+firstName+
        " lastName="+lastName+" age="+age+"]"
        }

これは、下記の JavaPerson から継承します。

リスト 13. これに見覚えはありませんか
// This is Java
        public class JavaPerson
        {
        public JavaPerson(String firstName, String lastName, int age)
        {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        }
        
        public String getFirstName()
        {
        return this.firstName;
        }
        public void setFirstName(String value)
        {
        this.firstName = value;
        }
        
        public String getLastName()
        {
        return this.lastName;
        }
        public void setLastName(String value)
        {
        this.lastName = value;
        }
        
        public int getAge()
        {
        return this.age;
        }
        public void setAge(int value)
        {
        this.age = value;
        }
        
        public String toString()
        {
        return "[Person: firstName" + firstName + " lastName:" + lastName +
        " age:" + age + " ]";
        }
        
        private String firstName;
        private String lastName;
        private int age;
        }

ScalaJavaPerson はコンパイルされると通常どおり JavaPerson を継承しますが、この場合にも Scala で要求されているとおり、ScalaObjectインターフェースも実装します。また ScalaJavaPerson は通常どおり、JavaPerson から継承されるメソッドもサポートします。ScalaJavaPerson は Scala 型であるため、以下のように Scala のルールに従って Any 参照への割り当てをサポートするものと考えることができます。

リスト 14. ScalaJavaPerson を使う
// This is Scala    
        val richard = new ScalaJavaPerson("Richard", "Campbell", 45)
        System.out.println(richard)
        val host : Any = richard
        System.out.println(host)

しかし、Scala で JavaPerson を作成し、同時にそれを Any 参照に割り当てようとした場合にはどうなるのでしょう。

リスト 15. JavaPerson を使う
        // This is Scala    
        val carl = new JavaPerson("Carl", "Franklin", 35)
        System.out.println(carl)
        val host2 : Any = carl
        System.out.println(host2)

このコードは想定どおりにコンパイルされ、動作することがわかります。これは、Any 型がjava.lang.Object型と似ているおかげで、JavaPerson は「適切なこと」をするものと Scala が暗黙のうちに保証しているためです。実際、java.lang.Object を継承するものはすべて Any 参照の中に保存することもできる、と言ってもほとんど問題ありません。(いくつか例外ケースがあると聞いていますが、私自身はこれまで実際にそうしたケースに遭遇したことはありません。)

実質的な結果はどうなのでしょう。現実的な目的から考えれば、あまり深く考えなくても Java 言語と Scala の両方にまたがって継承をうまく組み合わせることができます。(ただし Scala の「奇妙な名前を持つメソッド (^=!#など)」をオーバーライドする方法を考えることが大きな頭痛の種となるかもしれません。)

まとめ

今回は、Scala のコードと Java のコードが非常に似ているために Scala の継承モデルは Java 開発者にとって学びやすく、また理解しやすい、ということを説明しました。メソッドのオーバーライドの仕組みは同じであり、メンバーの可視性の仕組みは同じであり、等々です。Scala のすべての機能の中で、皆さん自身が Java 開発で経験してきたものとおそらく最も似ているものが継承と言えるでしょう。唯一、注意しなければならない点は、Scala の構文が Java とは大きく異なっていることです。

2 つの言語での継承の扱い方がほとんど同じ (そして少しだけ異なる) であることに慣れると、Java プログラムに対する独自の Scala 実装を楽に始めることができます。例えば、一般的な Java の基底クラスやフレームワーク (JUnit や サーブレット、Swing、あるいは SWT など) の Scala 実装を考えてみてください。実際、Scala のチームは OOPScala (「参考文献」を参照) と呼ばれる Swing アプリケーションを作成しました。OOPScala は JTable を使って驚くほどわずかな行数のコードで単純なスプレッドシート機能を提供しています。(従来の方法で Java で作成した場合よりもコード行の数は確実に 1 桁は少なくなっています。)

これまで、実際に皆さんが作成するコードに Scala を適用する方法がわからなかったのであれば、今やその方法を見つけるための第一歩を踏み出せるはずです。ともかく、皆さんの次期プログラムの一部を Scala で作成することを考えてみてください。今回の記事で学んだように、Java プログラムで行う場合とまったく同じように、問題なく適切な基底クラスから継承することができ、またオーバーライドを行えるはずです。


ダウンロード可能なリソース


関連トピック

  • ポッドキャスト: 「Scala revealed」(JavaWorld、2008年6月): developerWorks の寄稿者である Andrew Glover と Ted Neward が、関数型の言語とオブジェクト指向言語との違いについて、また並行処理やデータベース・プログラミングなど Java 言語や他の純粋なオブジェクト指向言語には適していないけれども重要な領域について語っています。
  • Scala for Java refugees Part 5: Traits and types」(Daniel Spiewak 著、Code Commit、2008年2月) でも Scala での継承の話題を学ぶことができます。
  • Implementation Inheritance with Mixins - Some Thoughts」(Debasish Ghosh 著、Ruminations of a Programmer、2008 年2月) は「現実的な妥協」を議論しながら、今日までの Java 言語での継承について説明しています。
  • A Tour of Scala: Higher-Order Functions」(Scala-lang.org) は Scala での高階関数を簡単な例で解説しています。
  • OOPScala」は Scala で作成された Swing アプリケーションです。
  • Functional programming in the Java language」(Abhijit Belapurkar 著、developerWorks、2004年7月) は、Java 開発者の視点から関数型プログラミングの利点と使い方を説明しています。
  • Scala by Example」(Martin Odersky 著、2008年5月) は、この記事で使用している Quicksort アプリケーションを含めて、コードを中心に Scala を簡潔に紹介しています (原文は PDF)。
  • Programming in Scala』(Martin Odersky、Lex Spoon、Bill Venners の共著、2008年2月 Artima より再版) は 1 冊の本の長さで Scala を紹介した最初の資料であり、Bill Venners が共著者として加わっています。
  • developerWorks の Java technology ゾーンには、Java プログラミングのあらゆる側面を網羅した記事が豊富に用意されています。
  • Scala をダウンロードしてください。現在のバージョンは 2.7.0-final です。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=317483
ArticleTitle=多忙な Java 開発者のための Scala ガイド: 実装継承
publish-date=05282008