Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

Scala 기본적인 내용 정리

요즘 스칼라 스터디를 하고 있습니다. 새로운 랭귀지라서 볼때는 그런가보다 하지만 막상 또 코드를 짜려고 보면 헷갈리기도 하고 잘 생각나지도 않아서 공부하는대로 정리합니다. (내용은 좀 두서없습니다.)



스칼라에 대해서
스칼라는 하이브리드 평셔널(Functional), 객체지향 언어로 Martin Odersky가 만들었으며 2003년에 첫 릴리즈를 했습니다.
아래는 스칼라의 특징들입니다.
  • 이벤트기반의 동시성 모델(event-based concurrency model)을 가지고 있다.
  • imperative스타일과 함수형스타일을 모두 지원
  • 순수한 객체지향이다.
  • 자바와 잘 섞어쓸수 있다.
  • 현명한 정적타이핑(static typing)을 지원한다
  • 간결하고 표현적이다
  • 작은 kernel에 작성되었다.
  • 높은 확장성과 함께 적은 코드로 고성능 애플리케이션을 만들수 있다.
자바와 다른점은 코드가 간결하면서 쓰레드와의 통신에 메시지 전달을 사용하기 때문에 wait(), notify()등의 메서드가 필요없습니다. Immutable 상태를 사용함으로써 데이터커넥션과 동기화에 시간을 들일 필요가 없습니다. Java에서는 int, double간은 primitive타입은 Object와 다르게 취급하지만 스칼라는 모든 것이 객체이기 때문에 2.toString() 같은 호출이 가능합니다. RichInt, RichDouble같은 클래스를 rich Wrapper클래스라 부르고 자바의 primitive타입을 표한하는데 사용합니다.
JavaBean을 만들기 위한 간단한 syntax를 제공하고 있으며 예외처리가 강제되지 않습니다.


기본적인 내용
변수의 정의는 아래와 같이 합니다.
var x = 1
val x = 1
var은 mutable타입으로 getter/setter 모두 생기며 val은 immutable타입으로 getter만 생기기 때문에 할당을 불가능하고 재정의는 가능합니다. 스칼라에서는 var보다는 val을 선호합니다.

import Actor._
import는 위와같은 형태로 하며 언더스코어(_)는 자바의 *과 비슷한 의미로 위의 라인은 자바에서 import Actor.*와 동일합니다.

for문

for (i <- 1 to 3) {
    print(i + ",")
}
println("Hello Scala!!")
// output -> 1,2,3,Hello Scala!!

for (i <- 1 until 3) {
    print(i + ",")
}
println("Hello Scala!!")
// output 1,2,Hello Scala!!

(1 to 3).foreach(i => print(i + ","))
println("Hello Scala!!")
// output 1,2,3,Hello Scala!!

for문에서 to는 마지막 범위까지 포함하고 until은 마지막 범위를 포함하지 않습니다.


package foo {
    package bar {
        class Baz {
       
 }
    }
}

위와같이 팩키지 중첩으로 작성하여 사용할수도 있으면 자바스타일의 팩키지 정의도 가능합니다.
모든 코드가 class안에 들어갈 필요 없기 때문에 바로 실행할 코드는 클래스밖에 넣으면 됩니다.

스칼라에서 할당(a=b)의 결과는 Unit입니다.(자바에서는 a의 값) 때문에 자바에서처럼 a=b=c를 정의하게 되면 b에는 c의 값이 들어가게 되지만 a에는 c가 아닌 Unit이 들어가게 됩니다.


옵션 사항
스칼라에서 쩜(.)과 괄호()는 파라미터가 1개나 없을 경우는 생략 가능합니다. 파라미터가 2개 이상일 경우에는 괄호는 꼭 있어야 하고 쩜(.)은 여전히 생략 가능합니다. 그래서 a + b 는 사실 a.+(b)와 같습니다.
return명령어는 옵션사항으로 return이 없을시에 함수의 마지막줄의 실행된 결과가 자동으로 리턴되게 되며 아래의 명령어처럼 return문이 없으면 리턴문의 타입을 스칼라가 추론(infer)합니다.
def check1() = true
def check2() : Boolean = return true

스칼라에서는 세미콜론(;)도 옵션사항으로 세미콜론은 코드를 읽는데 방해가 되기 때문에(아직은 확 와닿지는 않는군요.) 일반적으로 생략합니다.
클래스와 메서드는 기본이 public이기 때문에 public을 적지 않으면 public으로 선언이 됩니다.
스칼라에서 java.lang, scala, scala.Predef 팩키지는 기본으로 import하게 됩니다.


튜플 사용하기
def getPersonInfo(primaryKey : Int) = {
    ("Venkat", "Subramaniam", "venkats@agiledeveloper.com")
}
val (firstName, lastName, emailAddress) = getPersonInfo(1)
위와 같이 튜플을 사용할 경우 하나의 명령어로 여러개의 변수를 값이 각각 들어가게 되어 한번에 정의가 가능합니다.


"""..."""으로 멀티라인 스트링 정의
val str = """this is a test
             multiline"""
val str = """this is a test
             |multiline""".stripMargin
"""(쌍따옴표 3개)로 여러 라인에 스트링을 표현하는 것이 가능하며 stripMargin을 사용하면 |앞의 공백을 스트링에 포함하지 않게 할 수 있습니다. """는 이스케이프를 따로 해주지 않아도 되기 때문에 정규표현식을 정의할 때 유용합니다.
스터디중에 발견된 내용인데 """ 안에서 쌍따옴표를 사용할 수도 있는데 문자열의 첫번째 글자가 쌍따옴표라서 """"(쌍따옴표 4개)가 되면 오류가 발생합니다. 찾아본 결과 \등을 이용한 이스케이프를 지원하지 않고 오직 유니코드 이스케이프만 지원하기 때문에 첫글자에 쌍따옴표를 사용할 수 없습니다. 마지막의 쌍따옴표 4개는 이상없이 동작합니다. 이부분은 2.8.0 RC2에서는 수정되어 정상적으로 사용이 가능하다고 합니다.


Operator 오버로딩
스칼라는 opearator가 없기 때문에 오퍼레이터 오버로딩은 사실 +,-같은 이름을 가진 함수의 오버로딩을 의미합니다. 아래 코드와 같이 오퍼레이터에 대한 함수를 정의하여 객체간의 덧셈등을 가능하게 할 수 있습니다.

class Complex(val real:Int, val imaginary:Int){
    def +(operand:Complex):Complex={
        new Complex(real+operand.real,imaginary+operand.imaginary)
    }
    override def toString(): String ={
        real+(if (imaginary <0) "" else "+")+imaginary+ "i"
    }
}
val c1= new Complex(1,2)
val c2= new Complex(2,-3)
val sum=c1+c2
println("(" +c1+ ")+(" +c2+ ")=" +sum)
// output : (1+2i)+(2-3i)=3-1i

각 메서드들은 메서드의 첫번째 캐릭터로 메서드의 실행 우선순위를 결정하게 됩니다. 때문에 +와 *를 같이 사용하게 되면 일반적인 산수계산처럼 *가 먼저 처리 됩니다.
(아래로 갈수록 우선순위 높습니다.)
all letters
|
^
&
< >
= !
:
+ -
* / %
all other special charaters



==비교
자바에서는 ==비교가 primitive타입과 Object에서 다르게 동작합니다. ==는 primitive타입에서는 값비교이고 Object에서는 레퍼런스비교이기 때문에 Object에서는 equals를 사용하여 값비교를 하게 됩니다. 반면 스칼라에서는 모든 타입에서 ==를 사용하게 되고 ==는 값기반의 비교를 하며 타입은 신경쓰지 않습니다. 스칼라에서 레퍼런스의 identity기반의 비교를 하고 싶으면 eq()메서드를 사용하면 됩니다.

val str1= "hello"
val str2= "hello"
val str3= new String("hello")
println(str1 == str2) // Java의 str1.equals(str2)와 같음 -> True
println(str1 eq str2) // Java의 str1==str2와 같음 -> True
println(str1 == str3) // -> True
println(str1 eq str3) // -> False



기본 Access Modifier
스칼라의 Access Modifier는 자바와는 다릅니다. 자바에서 access modifier를 지정하지 않으면 팩키지 내부에서는 visible이지만 스칼라에서는 public입니다. 또한 자바는 현재 팩키지의 모든 클래스에 보여주거나 안보여주거나하는 all-or-nothing이지만 스칼라에서는 특정 클래스 혹은 팩키지를 지정해서 허용여부를 제한할 수 있습니다.
스칼라는 자바의 protected는 너무 관대하다고 생각하고 있습니다. 자바에서 protected는 현재 팩키지안의 모든 클래스와 다른 팩키지의 파생된 클래스에서 허용되지만 스칼라는 오직 파생된 클래스만 허용합니다. 그리고 파생클래스에서도 오직 자신의 타입에서만 protected멤버에 접근 가능합니다.(Car클래스에서는 Car객체로만 가능)
자바의 엔캡슐레이션은 클래스레벨이라서 자신의 클래스의 모든 오브젝트의 private 필드와 메서드에 접근할수 있지만 스칼라에서는 현재의 인스턴스로 제한할 수 있습니다.

스칼라에서는 주 생성자(primary constructor)을 private로 만들 수 있습니다.

private와 protected에 추가적인 파라미터를 전달하여 접근자를 제한할수 있습니다.(private[AccessQualifier] 처럼) AccessQualifier는 this(인스턴스에서만 visible) 둘러싸인 클래스나 팩키지 이름이 될 수 있다. 이 의미는 모두에게 private이고 AccessQualifier만 허용한다는 의미입니다.



스칼라의 특정부분보다는 전체에 대한 Preview적인 내용이라 정리만으로는 좀 내용파악이 어려운 부분들이 있습니다. 차후 계속 공부하는대로 추가정리해야 할듯 합니다.
2010/05/24 03:02 2010/05/24 03:02