Java
Scala

java経験者のScala入門メモ [関数、クラス]

More than 1 year has passed since last update.

前回のjava経験者のScala入門メモでは基本的な文法だとか、型についてまとめていたけど
なんやかんや、重要なのは今回かもしれない

関数

まずは関数からまとめてみる

宣言

なにやら種類がが2つあって
・「def」による関数
・関数オブジェクト
と分けられる見たい

今回はdefによる関数宣言をやっていく(関数オブジェクトがまだ分からない)

defを用いた宣言はこうやるらしい

def plus(a:Int,b:Int):Int = {
    return a+b
}

なんでも
def 関数名(パラメータ変数:型名, ....):戻り値の型 = {
〜処理〜
}
こういうこと

これを同じ内容でjavaで書くと

public int plus(int a,int b){
    return a+b;
}

こうなる

しかもscalaではreturnが省略できるのできる
ただしreturnが省略された場合最後に評価された式が返るのでこうなる

def plus(a:Int,b:Int):Int = {
    a+b
}

さらにさらに、戻り値の型にも型推論が使えるから
こうなる

def plus(a:Int,b:Int) = {
    a+b
}

さらにさらにさらに、今回は単純に値を返すだけの式が含まれていて
文は含まれていないのでここまで簡単になる

def plus(a:Int,b:Int) = a+b

もっとやると
パラメータがない場合こう書ける

def returnOne = 1

void型の戻り値

scalaでvoid、つまり戻り値がない場合どうすればいいか
こうなるらしい

def sayHello = {
    println("hello,world");
}

//こうも書ける
def sayHello = println("hello,world");

厳密には何も返さないわけではなく「()」という値を返しているらしい
これをvoidではなくScalaではUnit型というらしい
unit型の関数は=を省略し、{...}と書けるから

def sayHello {
    println("hello,world");
}

こうなる

デフォルト引数

引数にはデフォルト引数というのを定義できて
こうなる

def sayHello(name:String = "nameless") = {
    println("hello,"+name);
}

sayHello();//print -> hello,nameless
sayHello("John")//print -> hello,John

上の例でわかるように
デフォルト引数とは引数が渡されなかった場合に引数に格納される値で
デフォルト引数が設定されていようと、通常どおり引数を渡すことも出来る

名前付き引数

名前付き引数を使えば少しおもしろく書ける

def minus(a:Int,b:Int):Int = {
     return a-b
}

println(minus(b = 50,a = 100))//print -> 50

引数に名前をつけることで順番を変更できる

複数のパラメータリスト

名前だけ聞くと「は?」ってなるけど、例に上げてみれば簡単
今まで書いてきたplus関数をこう書くこともできる

def plus(a:Int)(b:Int) = a+b

plus(1)(1)//return -> 2
//これは引数が足りないからエラー
//plus(1)

ただカッコが増えただけっていう
ただこれはplus(a:Int,b:Int)と解釈されるから可能になる

可変長引数

これはどのプログラミング言語経験者でも聞いたことあると思う

Javaだとこう書く

public static void main(String... args){}

...が可変長引数であることを示しているけど
Scalaならこうなる

def plus(a:Int*) = {
    var i = 0
    val size = a.length
    while(i < size){
      println(a(i))
    }
}

型のあとに「*」をつければ可変長引数になる

名前渡し

これも複数のパラメータリスト同様java経験者には馴染みない機能

名前渡しと値渡し(?)の比較

//一般的な値渡しが↓
def sayHi(a:Any){}

//これが名前渡し
def sayHiName(a: => Any){}

名前渡しは
型名: => 型をパラメータとする

これらをこう呼び出すと

sayHi(println("hello"))//print -> hello

sayHiName(println("hello"))//print -> 何も表示されない

と、引数が関数内で使用されないと
その引数自体も評価されない

なので仮にsayHiNameを呼び出したときにprintln("hello")を呼び出したいならこうする

def sayHiName(a: => Any) {
    a//これでaを使用したことになる
}

暗黙のパラメータ

これもjava経験者にはわかりにくい
というか、イメージしにくい機能

この機能はなにかというと
関数の引数を省略できる

宣言方法

implicitを変数につける

def func(implicit n: Int) = println(n)

def func(implicit n:Int,str:String) = println(n+str)//これだとstrも暗黙のパラメータとして扱われる

//複数のパラメータリストを使うとこうなる
def func(n:Int)(implicit n2)= println(n+n2)

暗黙の値の定義

暗黙のパラメータだけ定義しても暗黙の値と言われるものを定義しないと何をどう省略すればいいか分からないので

暗黙の値を定義しなければ、暗黙のパラメータは使えない
そして、その定義方法は

implicit val n = 2

となる

これで引数を明示的に宣言しなくても暗黙の値が宣言されている暗黙のパラメータは実行される
もちろん暗黙のパラメータであっても通常と同じように引数を渡すことはできる

クラス

関数の次はクラスについてまとめてみる

クラスの宣言

javaと同様に「class」を使用する

java
class Hello{
}

scala

class Hello{
}

インスタンスの生成

これもjavaとほとんど同じ

前提として以下のクラスを定義する

java
class Hello{
    public void sayHello(){
        System.out.println("hello");
    }
}

scala
class Hello{
    def sayHello = println("hello")
}

これのインスタンスを生成する
javaの方では、別クラスのmainメソッドに記述していると仮定する

java
Hello hello = new Hello();
hello.sayHello();//print -> hello

scala
val hello = new Hello//コンストラクタがない場合カッコは省略できる
hello.sayHello//print -> hello

このようになる

コンストラクタ

javaではコンストラクタ関数を定義していたけど
scalaではコンストラクタを定義するのではなく「基本コンストラクタ」と呼ばれるものを
クラスの中身に直接書くらしい

java
class Hello{
    Hello(){//これがjavaのコンストラクタ
    }
}

scala
class Hello(_str:String){
    val sayhello = _str
    def sayHello = println(sayhello)
}

このようにScalaではクラス名の後に
(_変数名:型)のようにコンストラクタが受け取る値を指定して
クラス内部で直接、コンストラクタで行うような初期化処理を行うことが出来ます

さらに基本コンストラクタのパラメータ変数にはvalとvarをつけることが出来るらしい

補助コンストラクタ

補助コンストラクタというのもありjavaでのオーバロードに近いものらしい

メソッド名をthisにすることで補助コンストラクタとなる

scala
class Hello(_str:String){
     def this() = this("hello,world")
     val sayhello = _str
}

val hello = new Hello
hello.sayhello//get -> hello,world

クラス継承

クラス継承はjavaのextendsと同じ
ただし継承するスーパークラスのコンストラクタに引数を渡すときは

extends クラス名(引数)となる

内部クラス

内部クラスの宣言はjavaとおなじ

scala
class A{
    class B{
        val str:String = "hello"
    }
}

上記のクラスで内部クラスのインスタンスを生成したいときは
外側のクラスのインスタンスを生成し
そのインスタンス変数から内部クラスのインスタンスを生成するらしい

scala
val a = new A
val b = a.B

println(b.str)//print -> hello

抽象クラス

javaと同じようにscalaでも抽象クラスを宣言するときは
「abstract」を付ける

ただしjavaと違うのは、メソッドの抽象化にはabstractを付けないということ

finalクラス

javaと同じでfinalをclassの前につけるだけ
これでfinalがついたクラスを継承できなくなる