Skip to content

Instantly share code, notes, and snippets.

@Shinpeim
Created September 28, 2013 04:07
Show Gist options
  • Save Shinpeim/6738268 to your computer and use it in GitHub Desktop.
Save Shinpeim/6738268 to your computer and use it in GitHub Desktop.

Better Java としての Scala

Hello World

  • src/main/scala/Main.scala
object Main {
  def main(args: Array[String]): Unit = {
    println("hello scala!")
  }
}
  • build.sbt
name := "NiigataScala"

version := "0.1"

scalaVersion := "2.10.2"

ディレクトリ構成

  • ソースは普通は src/main/scala 以下に置く
  • Javaと一緒に使う場合、java ファイルは src/main/java に
  • jarに含めたいその他のリソース(設定ファイルとかそういうの)は src/main/resources に置く

println?

  • java のSystem.out.println 相当

object Main, def main?

このプログラムのエントリーポイントとなる部分のおまじない(と今は思っておいて)

hello world in REPL

ターミナルで scala と打つとREPL(Read Eval Print Loop)が立ち上がるので対話的にScalaが実行できる

hello world in scala script

scala コマンドに *.scala を食わせるとスクリプトとして実行できる

基本的な型とリテラル

"" で囲むと文字列(String)となる。数字はそのまま書くとIntになる。数字のあとにlを付けるとLong。少数を普通に書くとDouble''で文字を囲むとCharとなる。true, falseBoolean。プリミティブ型ってのはなくてすべてがオブジェクト。

変数と型推論

object Main {
  def main(args: Array[String]) = {
    val hello = "hello scala" //型宣言がない!
    println(hello)

    val hello2: String = "hello!" //型宣言しようと思えばできる
    println(hello2)

    // hello2 = "wrong!!" //val で宣言した変数には再代入ができない

    var hello3 = "hello scala!!"
    println(hello3)

    hello3 = "hello scala!!!" // varで宣言した変数には再代入ができる
    println(hello3)
    hello3 = 123 // 型が違うので代入できない
    
    var hello4:Any = "hoge"
    hello4 = 123 // String is a Any だし Int is a Any なので代入可能
  }
}

クラスの定義

フィールドの定義

class Player {
  val name = "しんぺい"
  var hp = 10
}

object Main {
  def main(args: Array[String]) = {
    val shinpei = new Player
    println(shinpei.name)
    println(shinpei.hp)

    shinpei.hp = 4 // var なので代入できる
    println(shinpei.hp)
  }
}

public なフィールドを定義しているように見えるけど、内部的にはアクセサを自動で生成してる。今後はこれを便宜的に「publicなプロパティ」と呼ぶ

privateにしてみる

class Player {
  val name = "しんぺい"
  private var hp = 10
}

object Main {
  def main(args: Array[String]) = {
    val shinpei = new Player
    println(shinpei.name)
    println(shinpei.hp) //コンパイルエラー

    shinpei.hp = 4 //コンパイルエラー
    println(shinpei.hp) //コンパイルエラー
  }
}

コンストラクタに引数を取りたい

// 引数の型は必ず指定する
class Player(_name: String) {
  // _name は private field扱いになるので外部に公開するために代入する
  val name = _name
  private var hp = 10
}

object Main {
  def main(args: Array[String]) = {
    val shinpei = new Player("しんぺい")
    val takashi = new Player("たかし")

    println(shinpei.name)
    println(takashi.name)
  }
}

あるいは

//コンストラクタのパラメータにval とかvar を付けるとパブリックなプロパティ扱いになる
class Player(val name: String) {
  private var hp = 10
}

object Main {
  def main(args: Array[String]) = {
    val shinpei = new Player("しんぺい")
    val takashi = new Player("たかし")

    println(shinpei.name)
    println(takashi.name)
  }
}
// private とかつければそうなる
class Player(val name: String, private var hp = 10)

object Main {
  def main(args: Array[String]) = {
    val shinpei = new Player("しんぺい")
    val takashi = new Player("たかし")

    println(shinpei.name)
    println(takashi.name)
  }
}

メソッドの定義

class Player(val name: String, private var hp = 10) {
  // 引数の型は必ず指定しなければならない
  // メソッドはデフォルトで public となる
  // 返り値の型は省略可能
  def attack(otherPlayer: Player) = println(name + "は" + otherPlayer.name + "を攻撃した!")
}

object Main {
  def main(args: Array[String]) = {
    val shinpei = new Player("しんぺい")
    val takashi = new Player("たかし")

    takashi.attack(shinpei)
  }
}

返り値の型を明示することもできる

class Player(val name: String, private var hp = 10) {
  // UnitはJavaでいうところのvoid
  def attack(otherPlayer: Player): Unit = println(name + "は" + otherPlayer.name + "を攻撃した!")
}

object Main {
  def main(args: Array[String]) = {
    val shinpei = new Player("しんぺい")
    val takashi = new Player("たかし")

    takashi.attack(shinpei)
  }
}

複数行にわたるメソッドの書き方,それと if

class Player(val name: String, private var hp = 10) {

  // 複数行に渡るメソッドは {} で囲む
  def attack(otherPlayer: Player): Unit = {
    println(name + "は" + otherPlayer.name + "を攻撃した!")
    otherPlayer.damaged(3)
  }

  def damaged(damage: Int): Unit = {
    println(name + "は" + damage.toString + "のダメージを受けた")

    // if が値を返している。
    hp = if (hp - damage < 0) { 0 } else { hp - damage }
    if (isDead) { println(name + "は死んでしまった" }
  }

  // return はいらない
  private def isDead: Boolean = hp <= 0
}

object Main {
  def main(args: Array[String]) = {
    val shinpei = new Player("しんぺい", 3)
    val takashi = new Player("たかし")

    takashi.attack(shinpei)
  }
}

クラスメソッド(staticメソッド)はどうすんの?

クラスメソッドってのは存在しない。それっぽい見た目のものは定義できる。

// Playerという名前のシングルトンオブジェクトを定義している。
// 初めて参照されたときにインスタンス化され、
// 二度目以降はそのインスタンスを参照する
object Player {
  val defaultPlayreName = "名無しマン"
  def createDefaultPlayer = new Player(defaultPlayerName)
}

class Player(val name: String, private var hp = 10) {
  def attack(otherPlayer: Player): Unit = {
    println(name + "は" + otherPlayer.name + "を攻撃した!")
    otherPlayer.damaged(3)
  }

  def damaged(damage: Int): Unit = {
    println(name + "は" + damage.toString + "のダメージを受けた")
    hp = if (hp - damage < 0) { 0 } else { hp - damage }
    if (isDead) { println(name + "は死んでしまった" }
  }

  private def isDead: Boolean = hp <= 0
}

object Main {
  def main(args: Array[String]) = {
    val shinpei = Player.defaultPlayer
    val takashi = new Player("たかし")
  }
}

継承

extends でできます

class Player(val name: String, private var hp: Int = 10) {
  val strength = 3

  def attack(otherPlayer: Player): Unit = {
    println(name + "は" + otherPlayer.name + "を攻撃した!")
    otherPlayer.damaged(strength)
  }

  def damaged(damage: Int) = {
    println(name + "は" + damage.toString + "のダメージを受けた")
    hp = if (hp - damage < 0) { 0 } else { hp - damage }
    if (isDead) { println(name + "は死んでしまった") }
  }

  private def isDead = hp <= 0
}

class Knight(name: String, hp: Int) extends Player(name: String, hp: Int) {
  override val strength = 5
}

class Magician(name: String, hp: Int, private var mp: Int = 5) extends Player(name: String, hp:Int) {
  def mera(otherPlayer: Player) = {
    println(name + "はメラを唱えた")

    if (mp < 3) {
      "しかしmpが足りない"
    } else {
      mp -= 3
      otherPlayer.damaged(10)
    }
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    val shinpei = new Magician("しんぺい", 10, 5)
    val takashi = new Knight("たかし",15)

    shinpei.mera(takashi)
    takashi.attack(shinpei)
    takashi.attack(shinpei)
  }
}

インターフェイス的なやつ

traitというのがそれです。実装も書けてマジクール。Java の インターフェイス と Ruby の Moduleの中間みたいな

trait Weapon {
  val strength: Int
}
trait KnightWeapon extends Weapon
trait MagicianWeapon extends Weapon

object NullWeapon extends Weapon {
  val strength = 0
}
object LongSword extends KnightWeapon {
  val strength = 10
}
object Rod extends MagicianWeapon {
  val strength = 2
}

trait HasWeapon {
  protected var weapon: Weapon = NullWeapon
}
trait CanEquipKnightWeapon extends HasWeapon {
  def equip(w: KnightWeapon) = weapon = w
}
trait CanEquipMagicianWeapon extends HasWeapon {
  def equip(w: MagicianWeapon) = weapon = w
}

class Player(val name: String, private var hp: Int = 10) extends HasWeapon {
  val strength = 3

  def attack(otherPlayer: Player): Unit = {
    println(name + "は" + otherPlayer.name + "を攻撃した!")

    val damage = strength + weapon.strength
    otherPlayer.damaged(damage)
  }

  def damaged(damage: Int) = {
    println(name + "は" + damage.toString + "のダメージを受けた")

    hp = if (hp - damage < 0) { 0 } else { hp - damage }

    if (isDead) {
      println(name + "は死んでしまった")
    }
  }

  private def isDead = hp <= 0
}

class Knight(name: String, hp: Int)
  extends Player(name: String, hp: Int) with CanEquipKnightWeapon {

  override val strength = 5
}

class Magician(name: String, hp: Int, private var mp: Int = 5)
  extends Player(name: String, hp:Int) with CanEquipMagicianWeapon {

  def mera(otherPlayer: Player) = {
    println(name + "はメラを唱えた")

    if (mp < 3) {
      "しかしmpが足りない"
    } else {
      otherPlayer.damaged(10)
    }
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    val shinpei = new Magician("しんぺい", 10, 5)
    val takashi = new Knight("たかし",15)

    shinpei.equip(Rod)
    shinpei.attack(takashi)

    takashi.equip(LongSword)
    takashi.attack(shinpei)
  }
}

型パラメータ

ところで、CanEquiq**Weapon、DRYじゃなくて気になる。型パラメータ使おう。

trait Weapon {
  val strength: Int
}
trait KnightWeapon extends Weapon
trait MagicianWeapon extends Weapon

object NullWeapon extends Weapon {
  val strength = 0
}
object LongSword extends KnightWeapon {
  val strength = 10
}
object Rod extends MagicianWeapon {
  val strength = 2
}

trait HasWeapon {
  protected var weapon: Weapon = NullWeapon
}
trait CanEquip[T <: Weapon] extends HasWeapon {
  def equip(w: T) = weapon = w
}

class Player(val name: String, private var hp: Int = 10) extends HasWeapon {
  val strength = 3

  def attack(otherPlayer: Player): Unit = {
    println(name + "は" + otherPlayer.name + "を攻撃した!")

    val damage = strength + weapon.strength
    otherPlayer.damaged(damage)
  }

  def damaged(damage: Int) = {
    println(name + "は" + damage.toString + "のダメージを受けた")

    hp = if (hp - damage < 0) { 0 } else { hp - damage }

    if (isDead) {
      println(name + "は死んでしまった")
    }
  }

  private def isDead = hp <= 0
}

class Knight(name: String, hp: Int)
  extends Player(name: String, hp: Int) with CanEquip[KnightWeapon] {

  override val strength = 5
}

class Magician(name: String, hp: Int, private var mp: Int = 5)
  extends Player(name: String, hp:Int) with CanEquip[MagicianWeapon] {

  def mera(otherPlayer: Player) = {
    println(name + "はメラを唱えた")

    if (mp < 3) {
      "しかしmpが足りない"
    } else {
      otherPlayer.damaged(10)
    }
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    val shinpei = new Magician("しんぺい", 10, 5)
    val takashi = new Knight("たかし",15)

    shinpei.equip(Rod)
    shinpei.attack(takashi)

    takashi.equip(LongSword)
    takashi.attack(shinpei)
  }
}

ちなみに、クラス定義のときじゃなくて new するときにも trait を mixin できる

trait Weapon {
  val strength: Int
}
trait KnightWeapon extends Weapon
trait MagicianWeapon extends Weapon

object NullWeapon extends Weapon {
  val strength = 0
}
object LongSword extends KnightWeapon {
  val strength = 10
}
object Rod extends MagicianWeapon {
  val strength = 2
}

trait HasWeapon {
  protected var weapon: Weapon = NullWeapon
}
trait CanEquip[T <: Weapon] extends HasWeapon {
  def equip(w: T) = weapon = w
}

class Player(val name: String, private var hp: Int = 10) extends HasWeapon {
  val strength = 3

  def attack(otherPlayer: Player): Unit = {
    println(name + "は" + otherPlayer.name + "を攻撃した!")

    val damage = strength + weapon.strength
    otherPlayer.damaged(damage)
  }

  def damaged(damage: Int) = {
    println(name + "は" + damage.toString + "のダメージを受けた")

    hp = if (hp - damage < 0) { 0 } else { hp - damage }

    if (isDead) {
      println(name + "は死んでしまった")
    }
  }

  private def isDead = hp <= 0
}

class Knight(name: String, hp: Int)
  extends Player(name: String, hp: Int) with CanEquip[KnightWeapon] {

  override val strength = 5
}

class Magician(name: String, hp: Int, private var mp: Int = 5)
  extends Player(name: String, hp:Int) with CanEquip[MagicianWeapon] {

  def mera(otherPlayer: Player) = {
    println(name + "はメラを唱えた")

    if (mp < 3) {
      "しかしmpが足りない"
    } else {
      otherPlayer.damaged(10)
    }
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    val shinpei = new Magician("しんぺい", 10, 5)
    val takashi = new Player("たかし",15) with CanEquip[Weapon]

    shinpei.equip(Rod)
    shinpei.attack(takashi)

    takashi.equip(LongSword)
    takashi.attack(shinpei)
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment