본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[Java8 In Action] #15 OOP 와 FP 의 조합 : 자바8과 스칼라 비교

by 돼지왕왕돼지 2019. 1. 4.

[Java8 In Action] #15 OOP 와 FP 의 조합 : 자바8과 스칼라 비교


Java8 In Action 내용을 보며 정리한 내용입니다.

정리자는 기존에 Java8 을 한차례 rough 하게 공부한 적이 있고, Kotlin 역시 공부한 적이 있습니다.

위의 prerequisite 가 있는 상태에서 추가적인 내용만 정리한 내용이므로, 제대로 공부를 하고 싶다면 책을 구매해서 보길 권장합니다!


anonymous function, apply, closure, Consumer, currying, first-class value, function, Function0, function22, immutable, Interface, java8 in action, java8 vs scalar, oop fp, oop fp 조합, option, Predicate, scalar function, scalar function1, scalar null, scalar primitive type, scalar 함수, trait, 객체지향, 게터와 세터 암시적 생성, 도메인 전용 언어, 디폴트 메서드, 명령형 스칼라, 스칼라, 스칼라 hello world, 스칼라 primitive type, 스칼라 트레이트, 스트림, 익명 함수, 일급 함수, 자바 vs 스칼라, 자바 스칼라, 커링, 클로저, 튜플, 패턴 매칭, 함수형 프로그래밍, 형식 추론


-

스칼라는 객체지향과 함수형 프로그래밍을 혼합한 언어다.

스칼라는 자바에 비해 많은 기능을 제공한다.

스칼라는 복잡한 형식 시스템, 형식 추론, 패턴 매칭, 도메인 전용 언어를 단순하게 정의할 수 있는 구조 등을 제공한다.

스칼라 코드에서는 모든 자바 라이브러리를 사용할 수 있다.


자바8과 마찬가지로 스칼라는 컬렉션을 함수형으로 처리하는 개념(스트림과 비슷한 연산), 일급 함수, 디폴트 메서드 등을 제공한다.

하지만 스칼라는 자바8에 비해 더 다양하고 심화된 함수형 기능을 제공한다.



15.1. 스칼라 소개


15.1.1. Hello beer


-

명령형 스칼라

object Beer{
    def main(args: Array[String]){
        var n:Int = 2
        while( n <= 6 ){
            println(s”Hello ${n} bottles of beer”)
            n += 1
        }
    }
}



-

함수형 자바

public class Foo { public static void main(String[] args){ IntStream.rangeClosed(2, 6).forEach(n -> System.out.println(“Hello “ + n + “ bottles of beer”)); } }


함수형 스칼라

object Beer{
    def main(args: Array[String]){
        2 to 6 foreach { n => println(s”Hello ${n} bottles of beer”) }
    }
}


-

스칼라에서는 모든 것이 객체다.

자바와 달리 스칼라에서는 기본형이 없다.

스칼라는 자바보다 완전한 객체지향 언어다.




15.1.2. 기본 자료구조: 리스트, 집합, 맵, 튜플, 스트림, 옵션


-

컬렉션 만들기

val authorsToAge = Map(“Raoul” -> 23, “Mario” -> 40, “Alan” -> 53)
val authors = List(“Raoul”, “Mario”, “Alan”)
val numbers = Set(1, 1, 2, 3, 5, 8)



-

불변과 가변


Map, List, Set 으로 만든 컬렉션들은 불변(immutable)이다.

일단 컬렉션을 만들면 변경할 수 없다.



-

컬렉션 사용하기

val fileLines = Source.fromFile(“data.txt”).getLines.toList() val linesLongUpper = fileLines.filter( line => line.length() > 10)                     .map(line => line.toUpperCase()) val linesLongUpper =fileLines filter(_.length() > 10) map(_.toUpperCase())



-

튜플


스칼라는 튜플 축약어, 즉 간단한 문법으로 튜플을 만들 수 있다.

val raoul = (“Raoul”, “+ 44 887007007’)
val alan = (“Alan”, “+44 88313700”)


스칼라는 임의 크기의 튜플을 제공한다. (최대 23개)

val book = (2014, “Java 8 in Action”, “Manning”) // (Int, String, String) tuple val numbers = (42, 1337, 0, 3, 14) // (Int, Int, Int, Int, Int) tuple



-

스트림


스칼라에서도 스트림이라는 게으르게 평가되는 자료구조를 제공한다.

스칼라의 스트림은 이전 요소가 접근할 수 있도록 기존 계산값을 기억한다.

또한 인덱스를 제공하므로 리스트처럼 인덱스로 스트림의 요소에 접근할 수 있다.

이러한 기능이 추가되면서 스칼라의 스트림은 자바 8의 스트림에 비해 메모리 효율성이 조금 떨어진다.

이전 요소를 참고하려면 요소를 “기억(캐시)” 해야 하기 때문이다.



-

옵션


스칼라의 Option 은 자바8의 Optional 과 같은 기능을 제공한다.

def getCarInsuranceName(person: Option[Person], minAge: Int) = 
    person.filter(_.getAge() >= minAge)
        .flatMap(_.getCar)
        .flatMap(_.getInsurance)
        .map(_.getName)
        .getOrElse(“Unknown”)



-

안타깝게도 자바와의 호환성 때문에 스칼라에도 null 이 존재한다.

하지만 되도록 null 을 사용하지 않는 것이 좋다.






15.2. 함수


-

스칼라의 함수는 어떤 작업을 수행하는 일련의 명령어 그룹이다.

자바에서는 클래스에 관련된 함수에 메서드라는 이름이 사용된다.




15.2.1. 스칼라의 일급 함수


-

스칼라의 함수는 일급값(first-class value) 이다.

즉 Integer, String 처럼 함수를 인수로 전달하거나, 결과로 반환하거나, 변수에 저장할 수 있다.

자바8의 메서드 레퍼런스와 람다 표현식도 일급 함수다.



-

def isJavaMentioned(tweet:String) : Boolean = tweet.contains(“Java”) // (T) => Boolean
def isShortTweet(tweet:String) : Boolean = tweet.length() > 20 // (T) => Boolean




15.2.2. 익명 함수와 클로저


-

스칼라도 익명 함수(anonymous function) 의 개념을 지원한다.

val isLongTweet: String => Boolean = (tweet:String) => tweet.length() > 60

사실 위 코드는 apply 메서드 구현을 제공하는 scala.Function1( 한 개의 인자를 받는 함수 ) 형식의 익명 클래스를 축약한 것이다.



-

자바에서는 람다 표현식을 사용할 수 있도록 Predicate, Function, Consumer 등의 내장 함수형 인터페이스를 제공했다.

마찬가지로 스칼라는 트레이트를 지원한다.

트레이트(trait)는 인터페이스와 비슷한 녀석이다.

스칼라에서는 Function0 (인수가 없으며 결과를 반환) 에서 Function22 (22개의 인수를 받음) 을 제공한다.

그리고 모두 apply 메서드를 정의한다.


컴파일러는 f(a) 라는 호출을 자동으로 f.apply(a) 로 변환한다.



-

클로저


콜로저(closure)란 함수의 비지역 변수를 자유롭게 참조할 수 있는 함수의 인스턴스를 가리킨다.

자바8의 람다 표현식에는 람다가 정의된 메서드의 지역 변수를 고칠 수 없다는 제약이 있다.

이들 변수는 암시적으로 final 로 취급된다.

즉, 람다는 변수가 아닌 값을 받는다는 사실을 기억하자.



-

스칼라의 익명 함수는 값이 아니라 변수를 캡처할 수 있다.

def main(args: Array[String]){
    var count = 0;
    val inc = () => count += 1
    inc()
    println(count) // 1
    inc()
    println(count) // 2
}



15.2.3. 커링


-

커링(currying)은 x,y 라는 두 인수를 가진 f 라는 함수가 있을 때 이는 하나의 인수를 받는 g 라는 함수, 그리고 g 라는 함수는 다시 나머지 인수를 받는 함수로 반환되는 상황으로 볼 수 있다는 것이다.

여러 인수를 가진 함수를 커링으로 일반화할 수 있다.

즉, 여러 인수를 받는 함수를 인수의 일부를 받는 여러 함수로 분할할 수 있다.



-

스칼라에서는 기존 함수를 쉽게 커리할 수 있는 방법을 제공한다.

def multiplyCurry(x:Int)(y:Int) = x * y
val r = multiplyCurry(2)(10)

자바와 달리 스칼라에서는 커리된 함수를 직접 제공할 필요가 없다.

스칼라에서는 함수가 여러 커리된 인수 리스트를 포함하고 있음을 가리키는 함수 정의 문법을 제공하기 때문이다.





15.3. 클래스와 트레이트


15.3.1. 간결성을 제공하는 스칼라의 클래스


-

게터와 세터


스칼라에서는 생성자, 게터, 세터가 암시적으로 생성되므로 코드가 훨씬 단순해진다.




15.3.2. 스칼라 트레이트와 자바 8 인터페이스


-

스칼라의 트레이트는 자바의 인터페이스를 대체한다.

트레이트로 추상 메서드와 기본 구현을 가진 메서드 두 가지를 모두 정의할 수 있다.

자바의 인터페이스처럼 트레이트는 다중 상속을 지원하므로 자바 8의 인터페이스와 디폴트 메서드 기능이 합쳐진 것으로 이해할 수 있다.

trait Sized{
    var size : Int = 0
    def isEmpty() = size == 0
}

class Empty extends Sized



-

자바 인터페이스와는 달리 객체 트레이트는 인스턴스화 과정에서도 조합할 수 있다.

class Box
val b1 = new Box() with Sized
println(b1.iEmpty())

val b2 = new Box()
b2.isEmpty() // compile error




15.4. 요약


-

자바 8과 스칼라는 객체지향과 함수형 프로그래밍 모두를 하나의 프로그래밍 언어로 수용한다.

두 언어 모두 JVM 에서 실행되며 넓은 의미에서 상호운용성을 갖는다.



-

스칼라는 자바 8처럼 리스트, 집합, 맵, 스트림, 옵션 등의 추상 컬렉션을 제공한다.

또한 튜플도 추가로 제공한다.



-

스칼라는 자바 8에 비해 풍부한 함수 관련 기능을 제공한다.

스칼라는 함수 형식, 지역 변수에 접근할 수 있는 클로저, 내장 커링 형식 등을 지원한다.



-

스칼라의 클래스는 암묵적으로 생성자, 게터, 세터를 제공한다.



-

스칼라는 트레이트를 지원한다.

트레이트는 필드와 디폴트 메서드를 포함할 수 있는 인터페이스다.




댓글0