2014년 3월 18일 Java 8가 릴리즈가 되었습니다.(참고 : https://en.wikipedia.org/wiki/Java_version_history#Java_SE_8)

자바 8에서 제가 관심있는 부분은 JVM 메모리 모델에서 Permanent generation이 없어지고 metaspace가 도입된 것과 functional programming style 지원을 위해 람다, Functional interface, Stream이 도입되고, Jodatime의 개념이 JSR 310에 녹아들었던 점입니다.


안드로이드에서는 RetroLambda로 Lambda expression을 사용할 수 있지만, Android는 N부터 Lamba expression, stream을 지원하고 있습니다. 안드로이드 개발자 태생이라 발등에 떨어진 불을 끄기 위해 늦게나마 lambda expression, stream, functional interface를 공부하여 이를 좀 정리해 보려합니다.


우선 첫번째 제가 정리하고 싶은 부분은 java.util.function에 정의된 Functional Interface와 Labmda expression, method reference간의 상호 매핑입니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package com.jayjaylab.test.testjava.java8;
 
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.function.*;
 
/**
 * Created by jjkim on 2016. 3. 18..
 */
public class TestFunction {
    public static void main(String ...args) {
        TestFunction testFunction = new TestFunction();
        String hello = "Hello, World!";
 
        // Consumer function
        System.out.println("consumer function");
        testFunction.<String>tryConsumer(
                s -> System.out.println(s), hello);
        testFunction.<String>tryConsumer(System.out::println, hello);
        System.out.println();
 
        // Predicate function
        System.out.println("predicate function");
        System.out.println(
                testFunction.<String>tryPredicate((s) -> s.startsWith("a"), hello));
        System.out.println(
                testFunction.<String>tryPredicate(hello::startsWith, "a"));
        System.out.println();
 
        // Function function
        System.out.println("function function");
        System.out.println(
                testFunction.<String, String>tryFunction((s) -> {
                    return s + " means nothing";
                }, hello)
        );
        System.out.println(
                testFunction.<String, String>tryFunction(s -> s + " means nothing", hello)
        );
        System.out.println(
                testFunction.<String, String>tryFunction(TestFunction::fillSuffix, hello)
        );
        System.out.println();
 
        // Supplier function
        System.out.println("supplier function");
        System.out.println(testFunction.<List>trySupplier(() -> new ArrayList()));
        System.out.println(testFunction.<List>trySupplier(ArrayList::new));
        System.out.println();
 
        // Unary Operator
        System.out.println("unary operator");
        System.out.println(testFunction.<BigInteger>tryUnaryOperator(
                (i) -> i.add(BigInteger.TEN), BigInteger.ONE));
        System.out.println(testFunction.<BigInteger>tryUnaryOperator(
                TestFunction::addTen, BigInteger.ONE));
        System.out.println();
 
        // BiFunction
        System.out.println("BiFunction");
        System.out.println(testFunction.<BigDecimal, BigDecimal, String>tryBiFunction(
                (t, u) -> t.add(u).toPlainString(), BigDecimal.ONE, BigDecimal.TEN)
        );
        System.out.println(testFunction.<BigDecimal, BigDecimal, String>tryBiFunction(
                testFunction::addAndReturnString, BigDecimal.ONE, BigDecimal.TEN
        ));
        System.out.println();
 
        // Binary Operator
        System.out.println("binary operator");
        System.out.println(testFunction.<String>tryBinaryOperator(
                (a, b) -> a + " and " + b, "cat", "dog"
        ));
        System.out.println(testFunction.<String>tryBinaryOperator(
                testFunction::concatenate, "cat", "dog"
        ));
        System.out.println();
    }
 
    public static BigInteger addTen(BigInteger number) {
        return number.add(BigInteger.TEN);
    }
 
    public static String fillSuffix(String msg) {
        return msg + " means nothing";
    }
 
    public String addAndReturnString(BigDecimal a, BigDecimal b) {
        return a.add(b).toPlainString();
    }
 
    public String concatenate(String a, String b) {
        return a + " and " + b;
    }
 
    // Typed parameters and return values
    public <T> void tryConsumer(Consumer<T> consumer, T t) {
        consumer.accept(t);
    }
 
    public <T> boolean tryPredicate(Predicate<T> predicate, T t) {
        return predicate.test(t);
    }
 
    public <T, R> R tryFunction(Function<T, R> function, T t) {
        return function.apply(t);
    }
 
    public <R> R trySupplier(Supplier<R> supplier) {
        return supplier.get();
    }
 
    public <T> T tryUnaryOperator(UnaryOperator<T> operator, T t) {
        return operator.apply(t);
    }
 
    public <T, U, R> R tryBiFunction(BiFunction<T, U, R> function, T t, U u) {
        return function.apply(t, u);
    }
 
    public <T> T tryBinaryOperator(BinaryOperator<T> operator, T t, T u) {
        return operator.apply(t, u);
    }
}

위 소스를 보시면 com.util.function의 대표적인 7개의 functional interface가 그에 대응하는 람다 표현식과 method reference 바인딩 하는 예제인데요. 우선 type parameter를 사용하는 인터페이스를 가지고 놀았는데, 다음에는 primitive type에 관한 functional interface와 그에 대응하는 람다 표현식, 메소드 참조를 가지고 놀아보겠습니다.


Posted by 제이제이랩
,

일상 #1

aboutMe 2016. 4. 16. 13:54


요즘은 오라클 performance engineer가 2014년에 펴낸 Java performance the definitive guide 란 책을 한번 더 보고 있습니다. 이 책의 난이도를 말하자면 자바에 대한 이해가 거의 최상 마스터급(?)이어야만 완전히 이해할 수 있다고 생각되는데요. 저는 아직 그 수준이 아닌지라 3번째 보고있지만 아직도 머리에 팍팍 이해가 안가는 내용이 있네요. 

여러 자바 관련 cpu, gc, memory, network 상태를 측정하는 툴 소개하고, JIT 컴파일러 튜닝, Oracle Hotspot JVM에 사용되는 4가지 GC Algorithm에 대한 설명과 튜닝 방법, Memory model에 대한 내용과 튜닝 방법, 실제 코드를 보여주면서 이렇게, 저렇게 하면 실제적으로 어떤 효과가 나타나는지 수치로 보여주고 있어 성능 튜닝 엔지니어가 될려면 바이블로 생각하셔도 될만큼의 깊이있는 책입니다.


이번 주말에 최대한 다보고 차후에 또 4번째 정독을 시도 해야겠네요. 이 책 보고나서는 Algorithm책의 정석이라는 MIT에서 나온 Introduction to algorithms 책을 볼 예정입니다.




소프트웨어 엔지니어로서 cpu, memory를 효율적으로 사용하기 위한 백그라운드 지식이 무엇이 필요할지 고민하다가 대학교 수업 때 공부한 자료구조와 알고리즘을 여러번 보고 연구를 해야겠다는 생각들어 읽을 예정입니다. 자료구조나 알고리즘에서 자주 따지는게 어떤 알고리즘의 시간 복잡도가 어떻게 되냐 이런 애기인데, 결국에는 cpu cycle 사용을 얼마나 최소화하냐 이런 애기지요. 공간 복잡도는 자주 거론되지 않는데 메모리 사용을 효율적으로 하려면 공간 복잡도에 대한 이해도 마스터급이 되어야 하기 때문에 다시 살펴보고 지식을 재정비할 필요가 있습니다. 본래 모바일 개발자로 일을 하다보니 공간 복잡도를 무시할 수가 없는 상태입니다;; 그런데 책이 1000페이지 이상이다보니 1년에 한번 정독 가능할 것 같네요. 


서비스나 제품 만들려면 아파치/Nginx 웹 서버 관련된 지식이 필요한데 서버 부분도 빨리 습득해야 되서 알고리즘 책을 1년 동안 주구장창 보고 또 보기란 쉽지 않네요. 아무튼 이래저래 전문연구요원할 때 빡세게 공부하면서 했어야 했는데 뒤늦게 뭘 하려다 보니 공부할 거리가 산더미만큼 쌓인지라 블로그에 글 쓰는게 점점 늦춰지고 있습니다. 개인적으로 재미로 Dependency Injector를 빨리 개발하고 싶은데, 공부 더미에 치이는 주말입니다.

'aboutMe' 카테고리의 다른 글

일상 #4  (0) 2016.07.16
일상 #3  (0) 2016.06.17
일상 #2  (0) 2016.05.23
2015 MobiCom, MobiSys program과 session 정보  (0) 2016.05.06
about me  (0) 2016.02.29
Posted by 제이제이랩
,

2016/03/06 - [java] - 추상화(Abstraction) 이해의 중요성 #2



추상화 이해의 중요성에 대한 3번째 글입니다. 2번째 글에선 자동차(Car)의 속성에 해당하는 Car.drivingDistance, Car.dateMadeAt, Car.Color, Car.Engine 등을 getter-setter method로 왜 Encapsulation(캡슐화)을 하는지 살펴보았습니다.


엔진(Engine)이라는 것도 생각해 보니 제조사가 어디인지 몇 마력인지 모델은 또 무엇인지 등 복잡한 정보를 가지고 있는 객체란 생각이 듭니다. Engine에 대한 추상화도 필요해 보입니다. 휠(Wheel), 제조사, 트렁크, 루프도 추상화가 필요해 보입니다. 쉽게 아래 테이블처럼 자동차 각 구성요소를  클래스로 추상화 할수 있습니다.


자동차 구성요소

추상(Abstraction) 

 엔진(Engine)

Engine 

 휠(Wheel)

Wheel

 제조사(Manufacturer)

Manufacturer

 트렁크(Trunk)

Trunk 

 루프(Roof)

Roof 


여기서 주의해야 할 사항이 있습니다. 추상화를 너무 과도하게 하지 않는가 생각해 보아야 합니다. primitive type, String으로 충분히 요구사항을 만족시킬 수 있고 추후 요구사항 및 변화에도 유연할 수 있다면  클래스화 하지 않을 수도 있습니다. Oracle HotSpot 32bit JVM 기준으로 한 객체는 8 바이트의 헤더를 가지고 있고 배열(Array)은  12 바이트의 헤더를 가지고 있습니다. 즉, 하나의 객체는 최소 8byte 메모리를 소모하고 하나의 배열은 최소 12byte 메모리를 소모 합니다. 메모리를 고려해야할 시스템 환경이라면 클래스 대신 byte, boolean, char, short, int, float, long, double과 같은 primitive type과 String으로 쓰는 것을 고려할 수있습니다. 원래 Car 클래스가 String 멤버 변수로 이루어진 것처럼 말이지요.


이 글에서는 윗 테이블처럼 추상화를 했다고 가정합니다. Car.color는 int로 변경하고 Car.dateMadeAt은 java.util.Date 또는 java 8의 java.time.LocalDateTime을 사용할 수 있습니다. 그러나 Car.dateMadeAt을 memory efficient하게 사용하기 위해 String으로 사용하고 java.util.DateTime 이나 java.time.LocalDateTime으로 변환하는 메소드를 제공하도록 하겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.jayjaylab.app.usedcar
 
public class Car {
    int drivingDistance;    // in km(killo meters)
    String dateMadeAt;    // in 2007-12-03T10:15:30 format
    int color;
    Engine engine;
    Wheel wheel;
    Manufacturer manufacturer;
    String model;
    Trunk trunk;
    Roof roof;
    // and so on...
 
    public void setEngine(Engine engine) {
        this.engine = engine;
    }
 
    public void setWheel(Wheel wheel) {
        this.wheel = wheel;
    }
 
    public void setRoof(Roof Roof) {
        this.roof = roof;
    }
     
    public void setDrivingDistance(int drivingDistance) {
        this.drivingDistance = drivingDistance;
    }
 
    public java.util.Date getDateMadeAt() {
        return new java.util.Date(dateMadeAt);
    }
     
    public java.time.LocalDateTime getLocalDateTimeMadeAt() {
        return java.time.LocalDateTime.parse(dateMadeAt);
    }
 
    ....
 
    public static void main(String[] args) {
        Car car = new Car();
        // 엔진 변경
        car.setEngine(new Engine("yamato"));
        car.setEngine(new Engine("superengine"));
        // 휠 변경
        car.setWheel(new Wheel("kumho_wheel"));
        car.setWheel(new Wheel("fantastic_wheel"));
        // 루프 변경
        car.setRoof(new Roof("default_roof"));
        car.setRoof(new Roof("sun_roof"));
        // 주행 거리 변경. 불법 이죠잉!
        car.setDrivingDistance(100);  // 100 km
        car.setDrivingDistance(200); // 200 km
    }
}

자동차 중고매매 사업장에서의 쓰일 자동차 Abstraction이긴 한데, 개발사 회사의 입장으로선 굳이 자동차 매매만 되는 애플리케이션으로 한정시킬 필요가 없을것 같습니다. 추후에 자동차 뿐만 아니라 오토바이, 자전거등의 사업장에서도 사용될 수 있는 확장성을 좀 더 고려하면 개발한 제품을 공급할 곳이 더 많아지겠지요. 그래서 저는 Vehicle이란 인터페이스를 만들어서 Car, Bike가 상속하게 해야할지 Buyable이란 인터페이스를 만들어서 Car, Bike가 상속하게 해야할지 고민이네요. Vehicle이란 인터페이스가 가질 Method는 go(), stop() 등의 자동차가 하는 행위일 것 같은데 사실 매매용 소프트웨어이기 때문에 자동차가 하는 행위 자체를 선언하는 것은 별 쓸모가 없어보이고 매매 시스템이니깐 매매되는 자동차라는 점에 중점을 두면 Buyable이란 인터페이스를 만들어 isSold(), buy()등의 매매 관련 메소드를 정의하는게 좀더 좋은 선택으로 보입니다.

그럼 한번 Buyable이란 인터페이스를 정의해 보겠습니다. 알아둬야 할 사항으로  자바에서는 대개 OOP의 Polymorphism(다형성)을 지원하기 위해 Interface을 정의하고 이를 상속하는 기법을 사용합니다. 특정 인터페이스를 상속하는 몇몇 클래스들이 인터페이스에 선언된 메소드의 정의가 동일하다면 그 인터페이스를 상속하는 Abstract 클래스를 정의하고 이 Abstract 클래스를 다시 상속하는 기법이 널리 쓰입니다. 그런데 Java 8에서는 interface에 default method와 static method란게 도입되어서 이젠 그런 기법이 서버쪽에서는 안쓰일 것 같네요. 서버 개발에 대한 실무 경험이 없어 장담하긴 힘듭니다.;; 안드로이드는 아직 Java 7만 지원이 되어서 java 8은 사실 고려 안해도 되구요.

[Java 8 Interface의 default methods, static methods 관련 글은 여기로]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.jayjaylab.app.usedcar
 
/**
 * 매매할 수 있는 객체를 나타냅니다.
 */
public interface Buyable {
    /**
     * 해당 상품이 다 팔렸는지 알기 위해 호출합니다.
     * @return true 이미 다 팔림, false 재고가 남아 있음.
     */
    boolean isSoldOut();
 
    /**
     * 구매할 때 호출합니다.
     * @param option 구매할 때 추가할 옵션들입니다.
     * @return true 구매 신청이 정상적으로 완료됨, false 구매 실패
     */
    boolean buy(Option option);
 
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.jayjaylab.app.usedcar
 
public class Car implements Buyable {
    int drivingDistance;    // in km(killo meters)
    String dateMadeAt;    // in 2007-12-03T10:15:30 format
    int color;
    Engine engine;
    Wheel wheel;
    Manufacturer manufacturer;
    String model;
    Trunk trunk;
    Roof roof;
    // and so on...
 
    public boolean isSoldOut() {
        return false;
    }
 
    public boolean buy(Option option) {
        return true;
    }
     
    ...
}


음, 위에 것처럼 구현하니 왠지 매매할 수 있을것 같은 자동차 Abtraction이 만들어진 것 같네요. 이번 글은 우선 여기까지하고 시간될 때 다시 돌아오도록 합죠.

Posted by 제이제이랩
,