前回までのプログラミング学習コラムに続き4回目の勉強内容です。
前回はScalaの関数型言語としての機能を紹介させて頂きましたが、今回はScalaのオブジェクト指向の機能を見ていきたいと思います。オブジェクト指向の機能と言うことでクラス、object[機能]、コンパニオン、トレイトを紹介させて頂きます。
クラス
Scalaのクラスは一点を除けば他の言語のものと大きく変わることはありません。その一点とは Java や C++ にある、静的●●( static
)を仕様できない点です。代わりに object
という機能を使うのですが、こちらは後ほど改めて紹介させて頂きます。ここではクラスの基本的な機能を流していきましょう。
- コンストラクタ
まず最初に、ScalaとJavaのコンストラクタで異なる点は多いです。 いつもどおり、サンプルコードを比較してみましょう。
// User.java public class User{ private final int id; private final String name; private int age; public User( int id, String name, int age){ this.id = id; this.name = name; this.age = age; } public int getId(){ return id; } public String getName(){ return name; } public void setAge( int age ){ this.age = age; } }
// User.scala class User( val id:Int, val name:String, var age:Int){ }
以上です。 解説しますと、Scalaのコンストラクタはクラス名の後に()を書く形で定義を行います。(省略可能) コンストラクタとして受け取った引数に var
もしくは val
が含まれている場合は自動でクラスのメンバー変数にします。 また、Scalaはアクセス演算子を指定しなければPublicになるので、GetterやSetterは設定しなくても使えます。
- アクセス演算子 基本的に関数・変数の前に
public
,private
,protected
を付ければ他の言語と同様に動作します。 - 継承 いつもどおりJavaのコードと比較させて頂きます。実際にコードを見て頂くとわかりやすいかと思います。
//Super.java public class Human{ protected String name = 0; public void printAge(){ println("my name is " + name) } } //Tanaka.java public class Tanaka extends Human{ public Tanaka(){ super(); name = "Tanaka"; } }
// Super.scala class Human{ protected var name def printAge = println("my name is " + name ) } class Tanaka extends Human(){ name = "Tanaka" }
いつもどおり、Extendsで継承することが可能で基底クラスの宣言の後に()で基底クラスのコンストラクタを呼び出せます。
- object
Scala のオブジェクトという機能を紹介させて頂きます。こちらはJavaやPythonなど他の言語には無い機能です。 デザインパターンのシングルトンをイメージして頂けるとわかりやすいと思います。 コードを比較しましょう。
//Sample.java class Sample{ private static Sample instance = null; private Sample(){} public static Sample getInstance(){ if(instance == null) instance = new Sample() return instance; } }
//Object.scala object Sample{}
以上になります。 objectを使うと1つだけのインスタンスのオブジェクトを作ることができます。 static がなくてもこちらを使えばインスタンスを生成せずにメソッドや変数を使用できます。 これはグローバル変数の代わりのように使用する事ができてしまうので、使用にあたっては細心の注意が必要になりますが、うまく使用するとすごく便利な機能になります。
- コンパニオン
先ほど、Scalaではstaticな静的●●が使用できないと言いましたが、半分嘘で半分本当です。 実はクラスとobjectを組み合わせるとコンパニオンという形でstaticを再現することができます。
object Companion{ val pub = "publicObject" private val pri = "privateObject" def printPub = println(pub) def main(arg:String[]){ val companion = new Companion companion.printAll } } class Companion{ val pub = "publicClass" private val pri = "privateClass" def printPubClass = println(pub) def printAll = println(Companion.pub + Companion.pri + pub + pri) }
これで静的変数を持つクラスを作ることができました。
- trait
トレイトは他の言語にもサポートされている機能でよくinterfaceと比較されます。 interface との大きな違いは実装を持つことができる点です。
trait Name{ val name = "hoge" def printName:Unit } trait Age{ val âge = 1 def printAge:Unit } class Human extends Name with Age{ def printName:Unit= println(name) def printAge:Unit=println(age) }
トレイトは実装も状態も持つことが可能です。 例えば、今回の例で言えばHumanの中でageやnameの中を書き換えると関数の実行結果も変わります。 関数の実装を全部トレイトに書いて、必要なメンバーの宣言だけトレイトをミックスインしたクラスで行うとすっきりとした設計にできるのではないかと思います。
まとめ
今回はScalaのオブジェクト指向の機能を中心に紹介させて頂きました。 ScalaのTraitやコンパニオン、クラスはとても奥深い機能で今回紹介できた部分はほんのわずかです。 ぜひとも、書籍『Scalaスケーラブルプログラミング』などScala本を読んでより深い所を探索していただければと思います。