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>" ) |
'스칼라' 카테고리의 다른 글
스칼라 13장 패키지와 임포트(Programming in Scala, 3rd) (0) | 2019.06.01 |
---|---|
스칼라 12장 트레이트(Programming in Scala, 3rd) (0) | 2019.06.01 |
스칼라 10장 상속과 구성(Programming in Scala, 3rd) (0) | 2019.06.01 |
스칼라 9장 흐름 제어 추상화(Programming in Scala, 3rd) (0) | 2019.06.01 |
스칼라 8장 함수와 클로저(Programming in Scala, 3rd) (1) | 2019.06.01 |