KoreanFoodie's Study
Scala 10 - Class 본문
스칼라 튜토리얼, scala의 class에 대해 알아보자
Class: Parameterized Record
object gee {
val a : Int = 10
def b : Int = a + 20
def f(z: Int) : Int = b + 20 + z
}
type gee_type = {val a:Int; def b: Int; def f(z:Int): Int}
class foo_type(x: Int, y: Int) {
val a : Int = x
def b : Int = a + y
def f(z: Int) : Int = b + y + z
}
val foo : foo_type = new foo_type(10,20)
If you define class, new type is made. The name really matters.
- use:
foo.a foo.b foo.f
foo
is a value offoo_type
gee
is a value ofgee_type
Class: No Structural Sub Typing
- Records: Structural sub-typing
foo_type <: gee_type
- Classes: Nominal sub-typing
gee_type <: foo_type
val v1: gee_type = foo
val v2: foo_type = gee // type error. WHY?
There's something special about
class
... The secret is Recursion!
Class: Can be Recursive!
class MyList[A](v: A, nxt: Option[MyList[A]]) {
val value: A = v
val next: Option[MyList[A]] = nxt
}
type YourList[A] = Option[MyList[A]]
val t: YourList[Int] =
Some(new MyList(3, Some(new MyList(4, None))))
Note on Null value
null
: The special element of every class & structural type
This value is needed to construct disjoint union types using classes in Java, which, however, is not as elegant and type safe as algebraic data types(ADTs):
-
Such disjoint union types can contain junk values (not elegant).
-
Null-point exception can be raised at run time (not type safe).
For this reason, it is discouraged to use null
in Scala although Scala supports null
for compatibility with Java.
Instead, it is encouraged to use ADTs, which themselves are classes and thus take advantages of both ADT and class.
Simplification using Argument Members
class MyList[A](v: A, nxt: Option[MyList[A]]) {
val value = v
val next = nxt
}
class MyList[A](val value: A, val next: Option[MyList[A]]) {
}
class MyList[A](val value: A, val next: Option[MyList[A]])
Type does not tell you it includes null or not. Therefore it may cause run time error if you try to put value on null type.
Simplification using Companion Object
class MyList[A](v: A, nxt: Option[MyList[A]]) {
val value = v
val next = nxt
}
object MyList { // val MyList = new
def apply[A](v: A, nxt: Option[MyList[A]]) =
new MyList(v, nxt) // This creating MyList "class"
} // This is purely record type!
type YourList[A] = Option[MyList[A]]
val t0 = None
val t1 = Some(new MyList(3, Some(new MyList(4, None))))
val t2 = Some(MyList(3, Some(MyList(4, None))))
// Without new, It tries to find Object, not class!
Last two sentences are actually the same!
- Exercise :
Define a class "MyTree[A]" for binary trees:
MyTree[A] =
(value: A)*
(left: Option[MyTree[A]]) *
(right: Option[MyTree[A]])
- Solution :
class MyTree[A](v: A,
lt: Option[MyTree[A]],
rt: Option[MyTree[A]]) {
val value = v
val left = lt
val right = rt
}
type YourTree[A] = Option[MyTree[A]]
val t0 : YourTree[Int] = None
val t1 : YourTree[Int] = Some(new MyTree(3, None, None))
val t2 : YourTree[Int] =
Some(new MyTree(3, Some (new MyTree(4,None,None)), None))
Nominal Sub Typing for Classes
- Nominal Sub Typing, a.k.a Inheritance
class foo_type(x: Int, y: Int) {
val a: Int = x
def b: Int = a + y
def f(z: Int): Int = b + y + z
}
class gee_type(x: Int) extends foo_type(x+1, x+2) {
val c: Int = f(x) +b
}
// gee_type <: foo_type
(new gee_type(30)).c // 188
def test(f: foo_type) = f.a + f.b
test(new foo_type(10,20)) // 40
test(new gee_type(30)) // 94
Overriding 1
class foo_type(x: Int, y: Int) {
val a: Int = x
def b: Int = a + y
def f(z: Int): Int = b + y + z
}
class gee_type(x: Int) extends foo_type(x+1, x+2) {
override def f(z: Int) = b + z
// or, override def f(z: Int) = super.f(z) * 2
val c: Int = f(x) +b
}
// gee_type <: foo_type
(new gee_type(30)).c
override def f(z: String): Int = 77 //No, arg: diff type
def f(z: String): Int = 77 //Yes, arg: diff type
override def f(z: Int): Nothing = ??? //Yes, ret: sub type
Overriding 2
class foo_type(x: Int, y: Int) {
val a : Int = x
def b : Int = a + y
def f(z: Int) : Int = b + y + z
}
class gee_type(x: Int) extends foo_type(x+1,x+2) {
override def b = 10
}
(new gee_type(30)).f(0) // 42
Example: My List
class MyList[A]()
class MyNil[A]() extends MyList[A]
class MyCons[A](val hd: A, val tl: MyList[A])
extends MyList[A]
val t: MyList[Int] =
new MyCons(3, (new MyCons(4, new MyNil())))
Example: MyList
class MyList[A]
class MyNil[A]() extends MyList[A]
object MyNil { def apply[A]() = new MyNil[A]() }
class MyCons[A](val hd: A, val tl: MyList[A])
extends MyList[A]
object MyCons {
def apply[A](hd:A, tl:MyList[A]) = new MyCons[A](hd, tl)}
val t: MyList[Int] = MyCons(3, MyNil())
def length(x: MyList[Int]) = ???
Case Class
class MyList[A]() { … }
case class MyNil[A]() extends MyList[A] { … }
//object MyNil { def apply[A]() = new MyNil[A]() }
case class MyCons[A](hd: A, tl: MyList[A])
extends MyList[A] { … }
//object MyCons {
// def apply[A](hd:A, tl:MyList[A]) = new MyCons[A](hd, tl)}
val t: MyList[Int] = MyCons(3, MyNil())
// + Pattern Matching
// Cf. sealed abstract class MyList[A]
Excercise
Define "MyTree[A]" using sub class.
class MyTree[A](v: A,
lt: Option[MyTree[A]],
rt: Option[MyTree[A]]) {
val value = v
val left = lt
val right = rt
}
type YourTree[A] = Option[MyTree[A]]
Solution
sealed abstract class MyTree[A]
case class Empty[A]() extends MyTree[A]
case class Node[A](value: A,
left: MyTree[A],
right: MyTree[A])
extends MyTree[A]
val t : MyTree[Int] =
Node(3, Node(4,Empty(),Empty()), Empty())
t match {
case Empty() => 0
case Node(v,l,r) => v
}
'Tutorials > Scala' 카테고리의 다른 글
Scala 11 - Abstract Class, Basics (0) | 2019.04.24 |
---|---|
Scala 9 - Sub Types (0) | 2019.04.23 |
Scala 8 - Parametric Polymorphism (0) | 2019.04.23 |
Scala 7 - Datatypes2 (0) | 2019.04.23 |
Scala 6 - Datatypes (0) | 2019.04.23 |