스칼라의 특성과 설계 철학
스칼라는 객체 지향 프로그래밍과 함수형 프로그래밍을 통합한 다중 패러다임 언어로, 데이터 처리 엔진인 Spark의 주요 개발 언어로 사용된다. 주요 특징은 다음과 같다:
- 다양한 프로그래밍 스타일 지원: 클래스 기반 객체지향과 일급 함수를 활용한 함수형 프로그래밍 모두 가능
- 자바와의 완전한 호환성: 자바 라이브러리 사용 및 상호작용이 자유롭게 이루어짐
- 간결한 문법: 타입 추론, 코드 줄 수 감소, 제어 구조의 고도화 제공
- 정적 타입 시스템: 컴파일 타임 검사로 안정성 향상, 리팩터링 시 안전성 확보
- 병렬 처리 강력 지원: 커스텀 제어 구조 생성 및 고성능 동시성 처리 가능
기본 프로그래밍 예제: "안녕하세요, 세상"
스칼라에서 실행 가능한 파일은 반드시 object로 정의해야 하며, class는 단순한 클래스 선언에 해당한다. 문장 종료에 세미콜론은 필요 없으며, 자바의 클래스/메서드도 직접 사용할 수 있다.
object Greeting {
def main(args: Array[String]): Unit = {
println("안녕하세요, 세상!")
System.out.println("또 다른 출력 방법")
}
}
변수와 상수
값이 변경 가능한 변수는 var, 변경 불가능한 상수는 val 키워드로 선언한다. 타입은 값에 따라 자동 추론되며, 명시적으로 선언할 수도 있다.
| 자바 | 스칼라 |
|---|---|
| byte | Byte |
| short | Short |
| int | Int |
| long | Long |
| float | Float |
| double | Double |
| boolean | Boolean |
| char | Char |
문자열 조작 방식
문자열 결합에는 + 연산자, StringBuilder, mkString 메서드, 또는 s"${변수}" 형태의 문자열 보간 기능이 존재한다. 후자는 내부적으로 StringBuilder를 사용하여 성능 최적화됨.
파일 입출력 처리
자바 방식으로 파일 읽기:
val reader = new BufferedReader(new FileReader("data/demo.txt"))
var line = reader.readLine()
while (line != null) {
println(line)
line = reader.readLine()
}
스칼라 전용 방식:
val source = Source.fromFile("data/demo.txt")
for (line <- source.getLines()) {
println(line)
}
※ Source.fromFile는 내부적으로 FileInputStream을 사용한다.
예외 처리
자바와 유사한 구조로 예외를 처리하며, try-catch-finally 문법을 지원한다.
try {
val result = 10 / 2
val arr = Array(1, 2, 3)
// 배열 범위 초과 발생 시 예외 발생
val input = scala.io.StdIn.readInt()
if (input == 0) throw new ArithmeticException("0으로 나누기 금지")
} catch {
case _: ArithmeticException => println("나눗셈 오류")
case _: ArrayIndexOutOfBoundsException => println("배열 인덱스 오류")
case _ => println("기타 오류 발생")
} finally {
println("종료 처리")
}
함수 정의 및 호출
함수는 def 키워드로 정의하며, 반환 타입과 매개변수 형식을 명시한다. Unit은 void와 동일하게 반환 값이 없다는 의미이다.
// 기본 함수 정의
def add(a: Int, b: Int): Int = a + b
// 단일 표현식은 중괄호 생략 가능
def multiply(a: Int, b: Int) = a * b
// 매개변수가 없을 경우 소괄호 생략 가능
def greet = println("안녕하세요!")
클래스와 객체
스칼라에서는 클래스를 통해 상태와 행동을 정의할 수 있으며, 생성자 파라미터는 클래스 본체 내에서 직접 사용된다.
class Person(val name: String, val age: Int) {
var gender: String = _
// 추가 생성자
def this(name: String, age: Int, gender: String) = {
this(name, age)
this.gender = gender
}
override def toString = s"이름:$name, 나이:$age, 성별:$gender"
}
// 객체 생성 및 호출
val person = new Person("홍길동", 25, "남성")
println(person)
샘플 클래스 (Case Class)
스칼라의 case class는 자동으로 생성자, equals, hashCode, toString 등을 생성하여 코드 작성량을 획기적으로 줄여준다.
case class Teacher(name: String, age: Int, var hobby: String)
val teacher = Teacher("김선생", 40, "공부")
teacher.hobby = "영화 보기"
println(teacher)
apply 메서드와 함께하는 객체 팩토리 패턴
객체에 apply 메서드를 정의하면, 객체 이름만으로 인스턴스 생성이 가능하다.
object Book {
def apply(title: String, price: Int) = new Book(title, price)
}
class Book(var title: String, var price: Int) {
override def toString = s"제목:$title, 가격:$price"
}
// 사용 예시
val book = Book("스칼라의 세계", 29000)
println(book)
함수형 프로그래밍의 핵심: 함수를 값처럼 다루기
스칼라에서는 함수 자체도 객체이며, 이를 변수에 할당하거나 다른 함수의 인자로 전달할 수 있다.
// 함수 타입 정의: String → Int
val stringToInt: String => Int = _.toInt
// 함수를 인자로 받는 함수
def process(f: String => Int, input: String): Int = f(input) + 100
// 사용 예시
val result = process(stringToInt, "200") // 결과: 300
고차원 함수 (Higher-Order Functions)
스칼라의 컬렉션은 다양한 고차원 함수를 제공한다.
foreach: 각 요소에 대해 처리, 반환 없음map: 각 요소를 변환하여 새로운 컬렉션 반환filter: 조건에 맞는 요소만 유지flatMap: 매핑 후 평탄화 (일반적인 변환 + 리스트 병합)groupBy: 특정 기준에 따라 그룹화
컬렉션 유형
- List: 순서 유지, 중복 허용, 불변
- Set: 순서 무관, 중복 불허, 불변
- Map: 키-값 쌍 저장, 키는 유일
- Tuple: 고정된 크기, 각 요소의 타입이 다를 수 있음 (최대 22개)
가변 컬렉션 사용
불변 컬렉션이 아닌 가변 컬렉션은 scala.collection.mutable 패키지에서 찾을 수 있다.
import scala.collection.mutable.ListBuffer
val buffer = new ListBuffer[Int]
buffer += 10; buffer += 20; buffer += 10
buffer -= 10 // 10 삭제
println(buffer.toList)
맵과 튜플 활용
맵은 키를 통해 값을 가져올 수 있으며, get 메서드는 Option을 반환하여 존재 여부를 안전하게 확인할 수 있다.
val scores = Map("Alice" -> 95, "Bob" -> 87)
println(scores.getOrElse("Charlie", 0)) // 0 반환 (없는 키에 대한 기본값)
실습: 단어 빈도 분석
텍스트 파일에서 각 단어의 등장 횟수를 집계하는 예제.
Source.fromFile("words.txt")
.getLines()
.toList
.flatMap(_.split("\\|"))
.groupBy(identity)
.mapValues(_.size)
.toList
.foreach { case (word, count) => println(s"$word: $count") }
확장 기능: JDBC 및 JSON 처리
JDBC 연결, 자바 컬렉션과 스칼라 컬렉션 간의 변환, match 식을 통한 패턴 매칭 등 실용적인 기능도 포함되어 있다.
암시적 전환 (Implicit Conversion)
스칼라의 암시적 전환은 타입 간 자동 변환을 가능하게 한다.
- 암시적 함수: 특정 타입을 다른 타입으로 자동 변환
- 암시적 클래스: 문자열을 특정 객체로 자동 변환 가능
- 암시적 변수: 함수의 인자에 암시적 값 제공