LoginSignup
6
1

More than 3 years have passed since last update.

class, case class, abstract class, trait, objectについて

Posted at

TL;DR

種類 概要
class いわゆる普通のクラス。
case class イミュータブルで、値で比較されるクラス。
abstract class 抽象メソッドを持てるけどインスタンス化できないクラス。
trait 抽象メソッドを持てるけどコンストラクタが持てない、インターフェース的な存在。
object それ自体が単一インスタンスのクラス。

コンパニオンオブジェクトとかシールドクラスとかとかとかはまた今度🍣

class

classはオブジェクトの設計図。

定義方法

最小の定義はclassキーワードと識別子のみ。

class User

val usr = new User

なお、命名は大文字はじまりのキャメルケース推奨。

メソッド

クラスのブロック内にdefを用いて定義。

class User {
  def greet(): Unit = println("Hello.")
}

val usr = new User
usr.greet() // Hello.

コンストラクタ

さきほどのclass Userは引数なしのデフォルトコンストラクタを持つ。引数を持つコンストラクタはクラスの識別子に続けて書く。この時、コンストラクタに指定された変数は、デフォルトでは、valで定義されたプライベートな変数となり、クラス内でのみアクセスできる。

class User(name: String, age: Int) {
  def greet(): Unit = println(s"Hello, I'm ${name}.")
  // 変更不可能
  // def changeAge(): Unit = age = age + 1
}
val usr = new User("rokumura", 30)

usr.greet() // Hello, I'm rokumura.

// アクセスできない
// println(usr.name)

パブリックなアクセスを持たせたい場合はvalまたはvarを付与する。
当然、varで定義した場合は変更が可能となる。

class User(name: String, var age: Int) {
  def greet(): Unit = println(s"Hello, I'm ${name}.")
  // varにしたため変更可能
  def changeAge(): Unit = age = age + 1
}
val usr = new User("rokumura", 30)

println(usr.age) // 30
// varを付与ことでパブリック且つ変更可能
usr.age = 10
println(usr.age) // 10
usr.changeAge()
println(usr.age) // 11

メンバー

クラス内に記述したメンバーはデフォルトでパブリックとなる。
プライベートにしたい場合はprivateを付与する。

class User {
  var x     = 100
  private var y = 999
}
val usr = new User
println(usr.x) // 100
// println(usr.y) // エラーとなる

case class

上述の普通のclassと似ているが、ちょっと違う。結構違うかもしれない。

定義方法

最小の定義はcase classキーワードと識別子パラメータリスト。パラメータリストは空でもできる。
ケースクラスはインスタンス生成を行うapplyメソッドをデフォルトで持っているため、生成時にはnewを必要としない。

case class Book()
val b = Book()

パラメータを用いてケースクラスを作った場合、その値はイミュータブル(=val)なパブリックの値として扱われる。
なお、varを用いてミュータブルにすることもできるが非推奨。

case class Book(isbn: String)
val book = Book("1234-5678")
println(book.isbn)
// イミュータブルなため変更不可
// book.isbn = "9876-5432"

比較対象

classの比較は参照比較であるのに対して、case classの比較は値比較である。

// クラスの場合、参照比較
class Dog(name: String)

val d1 = new Dog("poti")
val d2 = new Dog("poti")

// 値は同じであるが、参照が異なるため「違う犬」が出力される
if (d1 == d2) println("同じ犬")
else println("違う犬")

// ケースクラスの場合、値比較
case class cDog(name: String)

val c1 = cDog("poti")
val c2 = cDog("poti")

// 参照は異なるが、値は同じであるため「同じ犬」が出力される
if (c1 == c2) println("同じ犬")
else println("違う犬")

ただし、値が同じでも型が違えば違うと判定される。

case class cDog(name: String)
case class cCat(name: String)

val c1 = cDog("poti")
val c2 = cCat("poti")

// ケースクラスで値は同じだが、型が違うため「違う」が出力される
if (c1 == c2) println("同じ")
else println("違う")

abstract class

abstract classは、classが具象であるのに対して、抽象である。
classと基本同じだが、抽象メソッドを持つことができる。
また、抽象クラス(=abstract class)をインスタンス化することはできない。

定義方法

最小はabstract classキーワードと識別子。
継承する場合はextendsを用いる。

abstract class Engineer
class FrontendEngineer extends Engineer

抽象メソッド

抽象メソッドは振る舞いのみを定義し、実装は具象に任せる。
継承したクラスが具象クラスである場合、その振る舞いをoverrideして実装をしないとエラーとなる。

abstract class Engineer {
  // 振る舞いのみを宣言し、実装はしない
  def develop(): Unit
}

class FrontendEngineer extends Engineer {
  // 具象クラスで実装をしないとエラーとなる
  override def develop(): Unit = println("フロントエンドの開発をします")
}

val engineer = new FrontendEngineer()
engineer.develop() // フロントエンドの開発をします

trait

traitは、Javaのinterfaceっぽいもの。
抽象メソッドを持つことができ、前述のabstract classとも似ている。

定義方法

traitキーワードと識別子。マーカーインターフェースのように使う場合はこれ。
継承(インターフェース的に言えば実装と言ったほうが正しいか?)には、abstract classと同様に、extendsを用いる。

trait Engineer
class FrontendEngineer extends Engineer

abstract classとの違い

迷ったらtrait、必要になったらabstract class

継承

abstract classが単一継承であるのに対して、traitは複数実装することができる。

// abstract classの場合
abstract class Designer
abstract class Engineer
// 複数の抽象クラスを継承することはできない
class FrontendEngineer extends Engineer with Designer
// traitの場合
trait Designer
trait Engineer
// 複数のトレイトを実装することができる
class FrontendEngineer extends Engineer with Designer

なお、Mixinを用いれば、ごにょごにょはできる。

コンストラクタ

abstract classがパラメータを保有できるのに対して、traitは持てない。

// トレイトはパラメータを持てない
// trait Engineer(name: String)

abstract class Engineer(name: String)

object

一見classと似ているが、objectはそれ自体が単一のインスタンス。シングルトンと考えることができる。

定義方法

最小はobjectキーワードと識別子。

object Counter

classとの違い

先述の通り、objectはそれ自体が単一のインスタンスである。

object Counter {
  private var num = 0
  def add(): Unit = num = num + 1
  def show(): Unit = println(num)
}

Counter.add()
Counter.show() // 1
Counter.add()
Counter.add()
Counter.show() // 3
class Counter {
  private var num = 0
  def add(): Unit = num = num + 1
  def show(): Unit = println(num)
}

// クラスはインスタンス化が必要
val counter1 = new Counter
counter1.add()
counter1.show() // 1
counter1.add()
counter1.add()
counter1.show() // 3

// counter1とは別インスタンスであるため、値も共有されていない
val counter2 = new Counter
counter2.add()
counter2.show() // 1
counter2.add()
counter2.add()
counter2.show() // 3

参考

ぶっちゃけ下記リンクを一読したほうが良い🍣

6
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1