11.1 스칼라의 클래스 계층 구조

- 스칼라의 모든 클래스는 Any를 상속

 => Any에 정의된 메소드는 어느 객체에서도 호출 가능(보편적인 메소드)   

< Any에 정의된 보편적인 메소드 > 

final def ==(that: Any): Boolean 
final def !=(that: Any): Boolean 
def equals(that: Any): Boolean 
def ##: Int 
def hashCode: Int 
def toString: String

- "=="와 "!="는 final => 오버라이드 불가 => "=="는 eqauls와 같고 "!="는 equals를 반전시킨 것이기 때문에 "=="와 "!="를 재정의하고 싶으면 equals 오버라이드

 

- 루트 클래스 Any는 AnyVal, AnyRef 서브 클래스를 가짐

 

< AnyVal >

- 모든 스칼라 값 클래스의 부모 클래스(Byte, Short, Char, Int, Long, Float, Double, Boolean, Unit)

- 값 클래스는 스칼라에서 리터럴을 사용해 만들 수 있다.

- new를 사용해 인스턴스화 불가(모든 값 클래스는 추상 클래스인 동시에 final 클래스)

- Unit : 값을 반환하지 않는 메소드의 결과 타입으로 인스턴스 값 "()" 하나 존재

- 일반적인 산술/논리 연산자 제공

- 모든 값 클래스 간에 상속 관계는 없지만 값 클래스 타입 간에 암시적 변환 제공(scala.Int => scala.Long)

                ( 값 간의 타입 변환을 언어적인 스펙이 아니라 암시적 변환이라는 구현을 통해 제공 )

- 값 타입에 더 많은 기능을 제공하기 위해 암시적 변환 사용(min, max, until 등은 scala.Int => scala.runtime.RichInt) 

 

<AnyRef>

- 모든 참조 클래스의 기반 클래스

- 자바로 작성한 클래스나 스칼라로 작성한 클래스 모두 AnyRef 상속 => AnyRef == java.lang.Object

 

* Integer나 Long처럼 수를 박싱한 클래스는 "=="가 equals를 호출하지 않고 특별 취급, ##도 마찬가지

     Ex. 자바는 1 == 1L, new Integer(1) != new Long(1) 이지만 특별 취급 => new Integer(1) == new Long(1)

     Ex. new Integer(1)과 new Long(1)은 자바 hashCode는 다르지만 스칼라에서는 같은 값 반환

 

 

11.2 여러 기본 클래스를 어떻게 구현했는가?

- 자바와 마찬가지로 32비트 워드로 정수 저장 => JVM에서 효율적으로 실행 가능, 자바 라이브러리와 호환

- 덧셈, 곱셈 같은 표준 연산은 자바 기본 연산을 사용해 구현

- 정수에 toString() 메소드를 호출하거나 Any 타입의 변수에 정수를 할당하는 경우 java.lang.Integer로 투명하게 변환해 사용(스칼라 입장에서는 Int 클래스이지만 컴파일된 후 자바 바이트 코드 상에서 int primitive 타입으로 최대한 사용, 필요할 때 객체로 converting)

< 자바 >

boolean isEqual(int x, int y){

  return x == y;

}

sout(isEqual(1,1));      // True

 

boolean isEqual(Integer x, Integer y){

  return x == y;

}

sout(isEqual(1,1))       // False

 

< 스칼라 >

def isEqual(x:Int, y:Int) = x==y

isEqual(1,1)           // True

 

def isEqual(x:Any, y:Any) = x==y

isEqual(1,1)           // True

=> 자바는 원시 타입과 참조 타입 간에 차이 존재 but, 스칼라는 타입의 표현과 관계없이 동일하게 동작

val x = "abcd".substring(2)

val y = "abcd".substring(2)

x == y      // True ,  자바는 False

* 참조 동일성이 필요한 경우 "eq"와 "ne"를 사용

 

 

11.3 바닥에 있는 타입(Null, Nothing)

< scala.Null >

- null 참조의 타입

- 모든 참조 타입의 서브 클래스 => Null은 값 타입과는 호환성이 없다.

val i: Int = null    // 컴파일 오류

< scala.Nothing >

- 스칼라 클래스 계층의 맨 밑바닥 == 모든 타입의 서브 타입

- 값 존재 X

- 비정상적인 종료를 나타낼 때 사용

def error(message: String) : Nothing = throw new RuntimeException(message)

- Nothing이 다른 모든 타입의 서브 타입이기 때문에 다양한 곳에서 유연하게 사용 가능

def divide(x:Int, y:Int) : Int = 

    if(y!=0) x/y

    else error("can't divide by zero")

 

 

11.4 자신만의 값 클래스 정의

- 값 클래스를 만들기 위해선...

    1. val의 파라미터 필드 하나 존재

    2. def를 제외한 어떤 필드 존재 X

    3. AnyVal을 확장

    4. eqauls와 hashCode 재정의 X

class Dollars(val amount:Int) extends AnyVal {

    override def toString() = "$" + amount

}

 

val money = new Dollars(100000)     // money: Dollars = $100000

money.amount         // Int = 100000

- money는 컴파일된 자바 코드에서 amount 값을 가진 int 원시 타입으로 사용된다. toString()을 호출할 때 Dollars 타입으로 박싱된다.

     => 값 클래스는 자바 바이트 코드로 컴파일되면 내부 값 형태로 저장되고, 래퍼가 필요하면 그 때 자동으로 박싱/언박싱이 이루어진다.

 

- 스칼라의 클래스 계층을 가장 잘 활용하고 싶다면

=> 문제 영역에 잘 들어맞는 새로운 클래스를 정의

=> 동일한 클래스를 다른 목적에 재활용할 수 있는 경우에도 새로운 클래스 정의(아무리 작은 타입이라고 불릴지라도) 

== 스칼라에서는 클래스(타입)를 많이 사용하는 것을 권장

 

Ex. HTML을 만들어내는 코드

def title(text: String, anchor: String, style: String) : String = 

        s"<a id='$anchor'><h1 class='$style'>$text</h1></a>"

위와 같이 정의하면 아래의 경우를 컴파일러에서 막을 수 없다.

title("chap:vcls", "bold", "Value Classes")    // String = <a id='bold'><h1 class='Value Classes'>chap:vcls</h1></a> 

=> 작은 값 클래스 정의

class Anchor(val value: String) extends AnyVal

class Style(val value: String) extends AnyVal

class Text(val value: String) extends AnyVal

class Html(val value: String) extends AnyVal

 

def title(text: Text, anchor: Anchor, style: Style) : Html= 

        new Html( s"<a id='${anchor.value}'><h1 class='${style.value}'>$text.value</h1></a>" )

 

+ Recent posts