본문 바로가기

Student/모여서 각자 코딩

19년도 여름방학 모각코 7일차 회고 0718

목표 

scala 공부하기.

연산자

스칼라에서는 자바에선 불가능했던 식별자명을 식별자에 붙일 수 있다. 예를 들어 +라는 메소드를 정의하면 first.+(second)같은 식이 유효해진다. 여기에 더해 스칼라에선 a.f(b)를 a f b의 형태로 쓸 수 있기 때문에 first + second도 유효하다. 이런 식으로 자바에선 불가능했던 연산자 오버로딩을 할 수 있다.

 

암묵

암묵적으로 어떤 객체를 다른 타입으로 변환할 수 있다. 이것을 이용해서 위임을 편하게 만들어 원래 타입에 존재하지 않는 메소드를 애초에 있던 것처럼 호출할 수 있다. 예를 들어 자바의 BigInteger를 사용하려 하는데 .add()등을 사용하지 않고 암묵 변환을 이용해서 보조 클래스를 만들면 BigInteger에 + 등의 연산자를 적용할 수도 있다. 또한 암묵적으로 메소드 인자에 쓰일 값을 받을 수 있다. 별 생각 없이 쓰던 메소드의 정의를 살펴보면 듣도 보도 못한 인자 몇개가 붙어있는 것을 가끔 볼 수 있다.

 

이름으로 평가(call-by-name evaluation)

인자로 함수를 넘기다 못해 아예 코드 블록을 넘길 수 있다. 사실 구현 자체는 단순히 인자 없는 익명 함수에서 인자를 받는 부분을 생략하는 것 뿐이지만, 호출 측에서는 코드를 그대로 넘겨서 호출할 수 있다. 즉 아예 처음부터 언어에 있었던 것 같은 기능을 만드는 것도 가능하다.

트레이트(trait)

얼핏 보면 클래스는 아닌데 상속을, 그것도 여러 번 할 수 있는 점에서 자바 인터페이스와 비슷해 보일 수도 있다. 실제로 자바 인터페이스를 스칼라에서는 트레이트로 인식하며, 그냥 트레이트를 자바 인터페이스처럼 사용해도 좋다. 하지만 트레이트는 자바 인터페이스와는 달리 구체적인 구현을 담고 있으며, 믹스인이 가능하다. 예를 들어, Server extends Logger라는 클래스가 있다고 쳐보자. Server의 인스턴스를 만들 때, Server extends FileLogger라고 선언하면 파일로 로깅하며, Server extends NetworkLogger라고 하면 네트워크롤 통해 로그를 남긴다. 아니면 Server extends FileLogger with NetworkLogger라고 하면 파일과 네트워크에 모두 로그를 남길 수 있다(물론 상응하는 FileLogger와 NetworkLogger는 미리 작성해두어야 한다.). 즉 AOP를 언어 차원에서 지원한다. 또한 자바 인터페이스와 마찬가지로 구현하지 않은 메소드는 상속하는 쪽에서 구현해야 하므로 트레이트를 추상 클래스 급으로 만들어 놓고 믹스인으로 의존성을 주입할 수도 있다. 이런식으로 인터페이스처럼 보이지만 실은 믹스인할 수 있는 모듈이라고 보면 된다. 트레이트를 모듈로 쓰게 되면 with으로 트레이트를 쭉 쌓게 되는데, 여기서 착안해서 이런 방식을 케이크 패턴이라고 부른다.

 

생성자, 상속

클래스의 생성자, 슈퍼클래스 상속, 슈퍼클래스 생성자 호출이 클래스 선언과 융합(...)되었다. 보통 class 클래스명(인자1,인자2,...) extends 슈퍼클래스명(인자1, 인자2, ...){...}의 형태가 된다. 클래스명 옆의 인자 목록은 기본 클래스 생성자의 인자 목록이며, 슈퍼클래스명 옆의 인자 목록은 호출할 슈퍼클래스의 생성자에 해당하는 인자 목록이다. 이 외에 생성자를 오버로딩하고 싶다면 보조 생성자를 추가로 만들 수 있다. 자바와는 달리 보조 생성자의 이름은 무조건 this이다. 위 형태에서 기본 생성자의 본문이 어디있나 궁금할 수도 있는데, 클래스 본문 전체가 기본 생성자의 본문이 된다는 구성이다.

 

게터/세터 자동 생성

필드를 선언하면 그 필드에 따라 내부적으로 게터/세터가 생성된다. "객체.필드"이나 "객체.필드=값"으로 게터와 세터를 호출할 수 있다. 자동 생성된 게터와 세터는 명시적으로 오버라이드 가능하다. 단, 내부적으로 생성되는 게터/세터는 자바 스타일이 아니기 때문에 자바 쪽에서 쉽게 호출할 수 있게 하려면 @BeanProperty 어노테이션을 사용해야 한다.

 

싱글톤

object 키워드로 싱글톤 오브젝트를 만들 수 있다. 또한 스칼라에는 자바의 static에 대응하는 키워드가 없고 클래스와 같은 이름의 오브젝트(컴패니언 오브젝트)를 만들 수 있다. 때문에 자바에서라면 한 클래스 안에 존재했을 정적 코드와 클래스 코드가 자연스럽게 분리된다. 싱글톤을 원클릭으로 만들 수 있는 것도 장점이지만 static을 일일이 붙이지 않아도 되는 것도 사소한 장점이다.

 

타입 추론

변수의 타입, 함수의 반환값의 타입 등을 컴파일러가 추론해준다. 별 거 아닌 것 같아 보여도 코드 양이 줄어드는 데 큰 공여를 한다. 자바에서 Person p=new Person();이라고 썼던 것을 생각해보자. 스칼라에서는 같은 타입 이야기를 두번 하지 않아도 된다. 함수 반환값 추론은 사실 의식적으로는 거의 쓰이지 않는다. 재귀에서는 추론이 불가능하며, 재귀가 아니더라도 클래스의 public 함수에서는 타입을 명시하는 게 관행이기 때문. 함수 반환값 추론이 빛을 발할 때는 익명 함수를 사용할 때이다.

 

apply

apply는 C++에서 () 연산자를 오버로드하는 것을 떠올리면 된다. 예를 들어 배열을 임의 참조할 때 자바는 배열이 특수 객체로서 []을 사용할 수 있지만, 스칼라에서는 배열도 보통 객체 취급을 받으며 대신 apply가 구현되어 있어 ()로 임의 위치를 참조 가능하다. 또한 컴페니언 오브젝트에 apply를 구현해두면 생성자 대용으로도 쓸 수 있는데, 배열 등을 중첩할 때 new를 사용하지 않아도 되므로 편리하다. 심지어는 사실 함수 객체도 apply로 구현되어 있다(...).

패턴 매칭

굳이 따지자면 자바의 switch~case문에 대응한다. 겉으로 보이는 차이점은 fall-through가 없다는 점 정도지만, 가드와 unapply 메소드와 연계되어 switch문보다 복잡하게 실행된다. 사실상 unapply가 없으면 별 거 없는 그냥 시체가 된다.

 

unapply

unapply는 이름처럼 apply의 반대인데, apply처럼 괄호 안에 목록을 집어넣으면 그 안에 값을 집어넣는다. 즉 디스트럭처링(destructuring)을 손쉽게 하게 해 준다. 예를 들어, 조금 특수한 경우이기는 하지만, ("namu","나무위키")라는 순서쌍이 t라는 이름으로 선언되어 있을 때, (key, val)=t라고 써 넣으면 key와 val에 각각 "namu"와 "나무위키"가 들어간다. 이 예제처럼 단독으로 쓰일 수도 있지만, 보통은 패턴 매칭에서 구조를 분해하는 데 사용한다.