[Scala] Scala 기초 - 객체지향 프로그래밍 Object Oriented Programming

Hyunjun Kim·2025년 4월 15일
0

Data_Engineering

목록 보기
37/153

7. 객체지향 프로그래밍 Object Oriented Programming

7.1 클래스 Class

Scala는 객체지향 프로그래밍을 지원하기 위해 클래스를 제공한다.
타 언어와 비교했을 때 더 간소한 문법을 가지고 있다.

기본적으로 Java와 같은 keyword를 사용하는 특징들이 있다.

  • new 를 이용한 생성
  • extends 를 이용한 상속

Scala에서 클래스를 만드는 방법은 아주 간단하다. 아래 Book 클래스에는 책 제목 title과 저자 author가 멤버변수로 담겨져 있다.

class Book(var title: String, var author: String)

7.1.1 인스턴스 Instance

인스턴스는 클래스로부터 만들어진 각각의 객체를 뜻한다.
클래스가 설계도라고 하면 인스턴스는 그 설계도로 만들어진 각각의 물건이라고 생각하면 된다.

Scala 에서 인스턴스는 아래와 같이 만든다.

val myBook1 = new Book("My awesome book 1", "Me")
val myBook2 = new Book("My awesome book 2", "Me")

Scala 에서는 게터(getter)와 세터(setter) 가 자동으로 설정된다.
따라서 위의 클래스 정의 한 번으로 기본적인 기능들을 간단하게 사용할 수 있다.

예시

println(myBook1.title)
// 출력: "My awesome book 1"

myBook1.title = "My awesome book 1 updated!"
println(myBook1.title)
// 출력: "My awesome book 1 updated!"

위 기능을 제공하는 Java 클래스를 만드려고 했다면 Scala 보다 훨씬 긴 코드가 필요하다.

public class Book {
	private String title;
	private String author;

	public Book(String title, String author) {
		this.title = title;
		this.author = author;
	}

	public String getTitle() {
		return this.title
	}

	public String getAuthor() {
		return this.author
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public void setAuthor(String author) {
		this.author = author;
	}
}

위 코드가 Scala 에서는 단 한 줄로 표현이 가능하다.

class Book(var title: String, var author: String)

7.1.2 생성자 Constructor

이전에 클래스를 만들 때는 (변경 가능한 변수인) var을 사용했는데, (변경 불가능한 변수인) val 을 사용하면 필드를 read-only 로 만들 수 있다.

class Book(val title: String, val author: String) // var 이 아닌 val!

val myBook1 = new Book("My awesome book 1", "me")
myBook1.title = "My awesome book 1 updated!" // Not allowed!

Scala 에서 객체지향 코드를 작성할 때는 변수를 변경할 수 있도록 var 을 사용하자.
함수형 프로그래밍을 할 때는 이와 같은 클래스보다 나중에 설명할 case class 라는 것을 사용한다.

7.1.3 보조 생성자 Auxiliary Constructors

보조 생성자는 디폴트 생성자 외의 다른 방법으로 클래스 인스턴스 객체를 만드는 방법을 의미한다.
보조 생성자를 사용하면 클래스를 유연하게 만들 수 있다. this 메서드를 아래 규칙에 따라 정의함으로써 만들 수 있다.

  • this 보조생성자의 생성 변수 타입이 달라야 한다.
  • 이미 정의된 생성자를 (기본 생성자 포함) 사용해서 만들어야 한다.

이해가 어려우니 예시를 봐보자.

val DefaultAuthor = "me"

class Book(var title: String, var author: String) {
	def this(title: String) = {
		this(title, DefaultAuthor)
	}
}

val myBook1 = new Book("My awesome book 1", "me")
val myBook2 = new Book("My awesome book 2") // Works!

println(myBook2.author)
// 출력: "me"

이처럼 보조 생성자를 사용하면 기본값을 사용해 객체를 만들 수 있다.

7.1.4 기본값 설정과 명명변수 Named Parameters

이전 보조 생성자가 헷갈린다면 걱정할 필요 없다.
Scala 에서 기본값 설정은 쉬워서 보조 생성자를 사용할 일은 많이 없다.
또한 명명변수를 사용해서 코드를 더 깔끔하고 읽기 편하게 작성할 수 있다.

class Book(
	var title: String = "Default Title",
	var author: String = "me"
) {
}

val myBook = new Book()
val myBook2 = new Book(title="My Title") // 생성자 변수 이름을 지정해 준다.
// val myBook2 = new Book("My Title") 보다 더 정확한 의미 파악이 가능해진다.

println(myBook.title)
// 출력: "Default Title"

println(myBook.author)
// 출력: "me"

println(myBook2.title)
// 출력: "My Title"

7.2 Traits

trait (특성)는 Scala 의 아주 좋은 기능 중 하다나. Java 로 치면 Interface 와 비슷하지만 약간 다르다. trait 는 추상 클래스 (Abstract Class) 로도 사용할 수 있다.

7.2.1 특성 (Traits)

자바의 Interface 처럼 어떻게 사용되는지 간단한 예시를 먼저 보자. 우선 trait 을 정의한다.

trait Car {
	def engineStart(): Unit // Unit 은 Java 의 void 와 비슷하다고 생각하면 된다.
	def engineStop(): Unit
}

자동차는 엔진 시동을 걸 수 있고 끌 수 있다. 세단, SUV 모두 자동차이기에 Car traitextend 할 수 있다.

class Sedan extends Car {
	def engineStart(): Unit = println("Engine Start")
	def engineStop(): Unit = println("Engine Stop")
}

// 리턴타입 없이 아래처럼 작성해도 된다.
class SUV extends Car {
def engineStart() = println("Engine Start")
def engineStop() = println("Engine Stop")
}

7.2.2 다중 특성 (Traits Mixin)

Scala 에서는 with 키워드를 사용해 클래스에서 여러 특성을 사용할 수 있다. 예를 들어 버스와 택시같은 차들은 결제 기능도 있을 수 있다

trait PaymentModule {
	def collectPayment(amount: Int): Boolean
}

class Bus extends Car with PaymentModule {
	// Car
	def engineStart(): Unit = println("Engine Start")
	def engineStop(): Unit = println("Engine Stop")

	// PaymentModule
	def collectPayment(amount: Int): Boolean = {
		// 결제를 위한 기능을 여기서 수행합니다.
		return true
	}
}
def main(args: Array[String]): Unit = {
	val sedan: Car = new Sedan()
	val suv: Car = new Suv()
	val bus: Car = new Bus()

	val cars = List(sedan, suv, bus)
	cars.foreach(it => {
		it.engineStart()
		it.engineStop()
	})
	
    bus.asInstanceOf[PaymentModule].collectPayment(1600)
}

7.3. 추상 클래스(Abstract Class)

7.3.1 소개

Scala 의 추상 클래스는 Java 의 추상 클래스와 비슷하다.
하지만 대부분의 경우 특성 (Trait)으로 충분해서 추상 클래스는 많이 사용할 일이 없다.

추상 클래스는 아래와 같은 경우에만 사용한다.

  • 생성자 매개변수 (constructor parameters) 가 필요한 경우
trait Car(name: String) // Not allowed! 컴파일 에러
abstract class Car(name: String) // OK
  • Java 코드에서 호출이 되어야 하는 경우
    - Java 에서는 Scala trait 을 몰라서 Java 와의 호환성을 생각해야하면 abstract class 를 사용해야 한다.

7.3.2 사용법 (syntax)

추상 클래스는 특성과 비슷한 문법을 가지고 있다. 이전에 봤던 자동차 특성을 추상 클래스로 만들어 보자.

abstract class Car (name: String) {
	def engineStart(): Unit = println("Engine Start")
	def engineStop(): Unit = println("Engine Stop")

	def accelerate(): Unit // 추상 메소드
	def brake(): Unit = println(s"$name braking!")
}

추상 메소드는 Car 추상 클래스를 상속받은 객체에서 (예를 들면 Bus 클래스와 같은) 직접 정의를 할 수 있다. 추상 메소드가 아닌 경우에도 override 키워드를 통해 구현을 재정의 할 수 있다.

class Bus(name: String) extends Car(name) {
	override def engineStart() = println("Bus Engine Start")

	def accelerate() = println("Bus accelerating!")
}

val myBus = new Bus("402")
myBus.engineStart
// 출력: "Bus Engine Start"
myBus.accelerate
// 출력: "Bus accelerating!"
myBus.brake
// 출력: "402 braking!"

new Bus("402") 실행 시 흐름

  1. Bus의 생성자가 호출됨:
new Bus("402")
  1. class Bus(name: String)는 extends Car(name)를 통해 부모 생성자인 Car(name)에게 "402"를 전달한다.

  2. 따라서 Car 클래스의 생성자도 "402"를 매개변수 name으로 받아 초기화됨.

  3. 이 name은 Car 클래스의 메소드 brake() 안에서 사용됨:

def brake(): Unit = println(s"$name braking!")

개념 정리

  1. 주 생성자(primary constructor)의 인자 전달
    • Scala에서 클래스의 생성자 인자는 클래스 선언부에 직접 선언된다:
abstract class Car(name: String)
  • name은 생성자 인자일 뿐 클래스 내부 필드는 아님.
  • 그러나 클래스 내 메소드에서 접근 가능함 (단, val 또는 var로 선언하지 않으면 외부에서 접근은 불가).
  1. 하위 클래스에서 상위 클래스 생성자 호출

    • class Bus(name: String) extends Car(name)에서 Car(name)은 상위 생성자 호출.
    • Bus의 생성자에 들어온 "402"가 상위 클래스 생성자로 그대로 전달됨.
  2. name은 어디에 저장되어 있는가?

    • name: String은 생성자 매개변수이므로, 클래스 안에서 사용 가능하지만 필드가 아님.
    • 만약 외부 접근 혹은 서브 클래스 내부에서 접근하고 싶다면 다음과 같이 val로 바꿔야 함:
abstract class Car(val name: String)
profile
Data Analytics Engineer 가 되

0개의 댓글