템플릿 메소드 패턴(template method pattern)은 소프트웨어 공학에서 동작 상의 알고리즘의 프로그램 뼈대를 정의하는 행위 디자인 패턴이다. 알고리즘의 구조를 변경하지 않고 알고리즘의 특정 단계들을 다시 정의할 수 있게 해준다.

 

템플릿(template)은 하나의 '틀'을 의미한다. 하나의 틀에서 만들어진 것들은 형태가 다 같다. 이런 틀 기능을 구현할 때는 template method 패턴을 이용할 수 있다. 이는 상속의 개념이 있는 상위 클래스와 하위 클래스의 구조에서 표현할 수 있다. 일반적으로 상위 클래스(추상 클래스)에는 추상 메서드를 통해 기능의 골격을 제공하고, 하위 클래스(구체 클래스)의 메서드에서는 세부 처리를 구체화한다.

이처럼 상위 클래스에서는 추상적으로 표현하고 그 구체적인 내용은 하위 클래스에서 결정되는 디자인 패턴을 template method 패턴이라고 한다. 상속의 개념처럼 template method 패턴도 코드 양을 줄이고 유지보수를 용이하게 만드는 역할을 한다. 따라서 유사한 서브 클래스가 존재할 때 template method 패턴을 사용하면 매우 유용하다.

 

예를 한번 들어보자. 음료를 만들기 위한 클래스가 하나 있으면 음료수를 만들기 위해서는 1)컵을 준비한다. 2)물을 붓는다 3)첨가물을 넣는다. 4)음료를 내어드린다. 이렇게 4가지의 음료 만드는 과정이 있다. 1,2,4번 과정은 모든 음료를 만드는 데 공통적인 과정이라면 3번은 어떤 음료인가에 따라 첨가물이 달라질 것이다. 예를 들면 커피면 커피 가루, 홍차면 홍차 가루를 넣을 것이다.

 

이렇게 변경되는 로직부분을 추상 메소드로 만들어 놓고 상위 클래스에서는 알고리즘의 전체적인 틀을 만들고 하위 클래스에서는 구체적인 알고리즘의 일부를 구현하는 것이다.

 

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
/**
 * 공통 기능을 구현하고 세부 기능은 추상화한 추상클래스(음료제작)
 * @author yun-yeoseong
 * 
 */
public abstract class TemplateMethodPattern {
    
    public final void makeBeverage() {
        prepareCup();
        prepareWater();
        additive();
        finish();
    }
    
    /**
     * 공통 메소드
     */
    private void prepareCup() {
        System.out.println("컵을 준비한다.");
    }
    
    /**
     * 공통 메소드
     */
    private void prepareWater() {
        System.out.println("물을 붓는다.");
    }
    
    /**
     * 실제 구현이 필요한 부분
     */
    abstract void additive();
    
    /**
     * Hook 메소드, 서브클래스에서 구현이 필요하다면 오버라이드 해도된다.
     * 하지만 꼭 오버라이드가 강제는 아니다.
     */
    private void hookMethod() {
        System.out.println("hook method");
    }
    
    /**
     * 공통 메소드
     */
    private void finish() {
        System.out.println("음료를 내어드린다.");
    }
    
}
 
/**
 * 템플릿 추상 클래스를 상속하는 서브 클래스
 * 세부내용을 구현한다.
 * @author yun-yeoseong
 *
 */
public class SubClassA extends TemplateMethodPattern{
 
    @Override
    void additive() {
        System.out.println("커피가루를 넣는다.");
    }
    
    
}
 
/**
 * 템플릿 추상 클래스를 상속하는 서브 클래스
 * 세부내용을 구현한다.
 * @author yun-yeoseong
 *
 */
public class SubClassB extends TemplateMethodPattern{
 
    @Override
    void additive() {
        System.out.println("홍차가루를 넣는다.");
    }
    
    
}
 
cs

 

위와 같이 템플릿 메소드 패턴을 구현할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestMain {
 
    public static void main(String[] args) {
        
        TemplateMethodPattern template1 = new SubClassA();
        TemplateMethodPattern template2 = new SubClassB();
        
        template1.makeBeverage();
        System.out.println("=========================");
        template2.makeBeverage();
        
    }
 
}
cs

 

posted by 여성게
:

싱글톤(singleton)은 '단독 개체', '독신자'라는 뜻 말고도 '정확히 하나의 요소만 갖는 집합' 등의 의미가 있다. singleton 패턴은 객체의 생성과 관련된 패턴으로서 특정 클래스의 객체가 오직 한 개만 존재하도록 보장한다. 즉 클래스의 객체를 하나로 제한한다. 프로그램에서 이런 개념이 필요할 때는 언제일까? 프린터 드라이버의 예를 들어보자.

 

여러 컴퓨터에서 프린터 한 대를 공유하는 경우, 한 대의 컴퓨터에서 프린트하고 있을 때 다른 컴퓨터가 프린트 명령을 내려도 현재 프린트하는 작업을 마치고 그다음 프린트를 해야지 두 작업이 섞여 나오면 문제가 될 것이다. 즉 여러 클라이언트(컴퓨터)가 동일 객체(공유 프린터)를 사용하지만 한 개의 객체(프린트 명령을 받은 출력물)가 유일하도록 상위 객체가 보장하지 못한다면 singleton 패턴을 적용해야 한다. 이처럼 동일한 자원이나 데이터를 처리하는 객체가 불필요하게 여러 개 만들어질 필요가 없는 경우에 주로 사용한다.

 

사실 싱글톤을 구현하는 방법은 몇 가지 존재한다. 하지만 오늘 소개할 싱글톤 패턴 코드는 가장 많이 사용하고 안전한 싱글톤 객체를 만드는 코드 한가지만 다루어 볼것이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {
    
    private Singleton() {}
    
    private static class LazyHolder{
        public static final Singleton SINGLETON=new Singleton();
    }
    
    public static Singleton getInstance() {
        return LazyHolder.SINGLETON;
    }
    
}
cs

 

위 코드는 싱글톤으로 생성될 객체를 Lazy Loading 하는 코드이다. 클래스가 로딩될때 LazyHolder라는 내부 클래스는 로딩되지 않는다. 이유는 LazyHolder라는 객체를 참조하여 호출하는 코드가 존재하지 않기 때문이다. 그 말은 진짜 Singleton 객체가 필요하여 getInstance()를 호출할때 LazyHolder 클래스가 로딩되는 것이다. 바로 필요할때 객체를 생성한다는 장점이 있다.

 

또 하나의 장점이 있다. 해당 코드는 다른 싱글톤 생성 코드와 달리 싱글톤 인스턴스가 NULL인지 체크하는 것과 synchronized 블럭을 사용하여 동기화하는 부분도 존재하지 않는다. 왜 일까? 그러면 과연 멀티 스레드 환경에서 안전할까? 안전하다! JVM에서 클래스를 로딩하는 과정에서는 멀티 스레드 환경에서도 안전한 동기화 환경을 제공하기 때문이다! 즉, LazyHolder 클래스가 로딩되는 과정에 static 한 SINGLETON 인스턴스를 생성하기 때문에 싱글톤 객체 생성과정에서 별도의 동기화 처리를 하지 않아도 클래스 로딩되는 과정 자체가 JVM에서 동기화 환경을 제공하기 때문에 멀티 스레드 환경에서도 안전한 것이다!

 

LazyHolder를 이용한 싱글톤 객체 생성 시 장점

  1. 싱글톤 객체가 진짜 필요할 때까지 초기화를 늦춘다.(Lazy Loading)
  2. JVM 클래스 로딩 타임때 싱글톤 객체를 생성하므로 멀티 스레드 환경에 안전한 동기화 이슈를 JVM이 직접 해결해준다.

싱글톤 객체를 생성할 때는 위의 방법을 이용하자!

 

 

기타 싱글톤 객체 생성 코드

나머지 방법들은 멀티 스레드에 안전하지 않으면서도 불필요한 Lock을 잡고 있는 방법들이기 때문에 사용을 권장하지 않는다.

 

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
public class Singleton_methodSync {
    
    private static Singleton_methodSync instance;
 
    private Singleton_methodSync() {}
    
    public static synchronized Singleton_methodSync getInstance() {
        if(instance == null) {
            instance = new Singleton_methodSync();
        }
        return instance;
    }
    
}
 
//Double Checked Lock
public class Singleton_methodSync2 {
    
    private static Singleton_methodSync2 instance;
    
    private Singleton_methodSync2() {}
    
    public static Singleton_methodSync2 getInstance() {
        if(instance == null) {
            synchronized (Singleton_methodSync2.class) {
                if(instance==null) instance = new Singleton_methodSync2();
            }
        }
        return instance;
    }
}
 
//이른 초기화
public class Singleton_precreate {
    
    private static Singleton_precreate instance = new Singleton_precreate();
    
    private Singleton_precreate() {    }
    
    public static Singleton_precreate getInstance() {
        return instance;
    }
    
}
 

public class Singleton_notsafety {        

private static Singleton_methodSync instance;

    private Singleton_methodSync() {}    

    public static Singleton_methodSync getInstance() {

        if(instance == null) {

            instance = new Singleton_methodSync();

        }

        return instance;

    }    

}

 cs
posted by 여성게
:

퍼사드패턴 (facade pattern)

어떤 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공한다.
퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브시스템을 더 쉽게 사용할수 있다.

 

퍼사드(Facede)는 '건물의 앞쪽 정면(전면)'이라는 사전적인 뜻을 가진다. 퍼사드패턴은 위의 그림과 같은 구조로 이루어지는 디자인패턴이다. 간단히 위의 그림을 설명하면 몇 개의 복잡한 서브시스템들과 클라이언트 사이에 Facede라는 객체를 세워놓음으로써 복잡한 관계를 정리 혹은 구조화하는 패턴이다. 

 

예를 들면 영화를 보기 위한 클라이언트가 있다. 조금은 억지스러운 예제이지만, 서브시스템으로는 Movie,Beverage라는 인터페이스와 이 인터페이스를 구현할 클래스가 있다.(영화를 보기 위해 음료를 구입하고 영화를 키고 음료를 다 마신다. 영화가 끝나면 영화를 끄고 다 먹은 음료컵을 버린다.) 물론 더 많은 서브시스템이 붙을 가능성도 있다. 퍼사드패턴을 적용하기 전에는 클라이언트가 영화를 보기위해서는 클라이언트 코드에 Movie,Beverage 등의 서브시스템을 의존하여 일일이 음료를 사고 영화를 보러 들어가고 등의 로직을 직접 호출해야한다.(만약 복잡한 서브시스템들이 더 많다면 더 많은 호출이 이루어질 것이다.) 그리고 하나의 기능을 수행하기 위해 여러개의 서브시스템들이 조합되어야 한다면 클라이언트는 여러개의 서비스를 직접 호출하여 로직을 만들어야 할 것이다.

 

그래서 퍼사드 패턴을 이용하여 모든 관계를 전면에 세워진 Facede 객체를 통해서만 이루어질 수 있게 단순한 인터페이스를 제공하는 것이다. 퍼사드 패턴을 이용하면 서브시스템 내부에서 작동하고 있는 많은 클래스들의 관계나 사용법을 의식하지 않고 퍼사드 객체에서 제공하는 단순화된 하나의 인터페이스만 사용하므로, 클래스 간의 의존 관계가 줄어들고 복잡성 또한 낮아지는 효과를 볼 수 있다.

 

여기서 퍼사드 객체는 클라이언트의 요청이 발생했을 때, 서브시스템 내의 특정한 객체에 요청을 전달하는 역할을 한다. 이 역할을 수행하려면 퍼사드 객체는 서브시스템의 클래스들에 어떤 기능이 있는지 알고 있어야 한다.(인스턴스 필드에 선언) 즉, 서브시스템은 자신의 기능을 구현하고 있으면 되고, 퍼사드 객체로부터 자신이 수행할 행위에 대한 요청을 받아 처리하면 되는 것이다. 또한 서브시스템은 퍼사드 객체의 어느 정보도 알고 있을 필요가 없다.

 

Facede Pattern의 장점

  • 퍼사드는 소프트웨어 라이브러리를 쉽게 사용할 수 있게 해준다. 또한 퍼사드는 소프트웨어 라이브러리를 쉽게 이해할 수 있게 해 준다. 퍼사드는 공통적인 작업에 대해 간편한 메소드들을 제공해준다.
  • 퍼사드는 라이브러리를 사용하는 코드들을 좀 더 읽기 쉽게 해준다.
  • 퍼사드는 라이브러리 바깥쪽의 코드가 라이브러리의 안쪽 코드에 의존하는 일을 감소시켜준다. 대부분의 바깥쪽의 코드가 퍼사드를 이용하기 때문에 시스템을 개발하는 데 있어 유연성이 향상된다.
  • 퍼사드는 좋게 작성되지 않은 API의 집합을 하나의 좋게 작성된 API로 감싸준다.

 

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
/**
 * 음료관련 서브시스템
 * @author yun-yeoseong
 *
 */
public interface BeverageService {
    
    public void prepare();
    public void takeout();
    public void gotoTrash();
    
}
 
public class BeverageServiceImpl implements BeverageService {
 
    @Override
    public void prepare() {
        System.out.println("음료준비");
    }
 
    @Override
    public void takeout() {
        System.out.println("음료주기");
    }
 
    @Override
    public void gotoTrash() {
        System.out.println("다먹은 음료컵 버리기");
    }
 
}
 
/**
 * 영화관련 서브 시스템
 * @author yun-yeoseong
 *
 */
public interface MovieService {
    
    public void prepare();
    public void turnOn();
    public void turnOff();
}
 
public class MovieServiceImpl implements MovieService {
 
    @Override
    public void prepare() {
        System.out.println("영화준비");
    }
 
    @Override
    public void turnOn() {
        System.out.println("영화틀기");
    }
 
    @Override
    public void turnOff() {
        System.out.println("영화끄기");
    }
 
}
 
 
/**
 * 영화,음료 시스템의 인터페이스를 하나의 인터페이스로
 * 몰아서 더 쉽게 서브 시스템들을 사용할 수 있게한다.
 * @author yun-yeoseong
 *
 */
public interface MovieFacede {
    
    public void prepareWatchMovie();
    public void watchMovie();
    public void endMovie();
    
}
 
public class MovieFacedeImpl implements MovieFacede {
    
    private MovieService movieService;
    private BeverageService beverageService;
    
    public MovieFacedeImpl(MovieService movieService,BeverageService beverageService) {
        
        this.movieService=movieService;
        this.beverageService=beverageService;
        
    }
    
    @Override
    public void prepareWatchMovie() {
        
        beverageService.prepare();
        beverageService.takeout();
        movieService.prepare();
        
    }
 
    @Override
    public void watchMovie() {
        
        movieService.turnOn();
        
    }
 
    @Override
    public void endMovie() {
        
        movieService.turnOff();
        beverageService.gotoTrash();
        
    }
 
}
cs

 

만약 Spring fw을 사용한다면 퍼사드 객체에 DI해주는 어노테이션을 붙이면 클라이언트에서 직접 의존 주입해줄 필요가 없어진다. 예제가 조금 억지스러운 면이 없지않지만 대략적으로 퍼사드패턴을 사용하는 이유와 사용법이 전달되었으면 좋겠다.

posted by 여성게
:

 

Archive for required library 와 같은 메시지와 함께 이클립스 빌드패스 문제가 있을 때가 있다. Update Project를 해보고, Maven clean & install을 해보아도 문제가 해결되지 않았다.(여기까지로 해결되는 경우도 종종있음)

해당 에러 메시지를 보면 특정 메이븐 경로에 있는 특정 라이브러리가 눈에 띌 것이다. 해당 경로를 들어가서 폴더를 싹 지운 후에 해당 프로젝트 우클릭->Maven->Update Project 하면 문제가 말끔히 해결된다.

posted by 여성게
:

 

RestTemplate으로 다른 API를 호출하고 특정 객체 타입으로 JSON을 parsing 하는 상황이었다. 그런데 해당 특정 객체는 내부적으로 Inner Class를 가지고 있는 상황이었는데, 아래와 같은 예외가 발생하였다.

 

예외:can only instantiate non-static inner class by using default no-argument constructor

 

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
@Getter
@Setter
@ToString
public class Outer {
    
    @Getter
    @Setter
    @ToString
    @NoArgsConstructor
    public class Inner1{
        
        private String[] a;
        private double[] b;
    }
    
    @Getter
    @Setter
    @ToString
    @NoArgsConstructor
    public class Inner2{
        
        private String[] a;
    }
    
    private long id;
    private Inner1[] a;
    private Inner2[] b;
    private int status;
    
}
cs

 

무슨 일 일까? 분명 매개변수 없는 생성자로 생성하였는데, 위와 같은 예외가 발생했다니... 원인은 바로 Inner Class가 static(정적)으로 선언되지 않는 한 단독(Outer 클래스를 참조하지 않고)으로 Inner Class의 디폴트 생성자를 호출해 인스턴스를 생성할 수 없는 것이다. 즉, 위와 같은 예외를 피하려면 Inner Class를 별도의 클래스로 생성하던가, 아니면 static Inner Class로 선언해주어야 한다. 

posted by 여성게
:

 

"Uncaught Error: Bootstrap's JavaScript requires jQuery at" 와 같은 에러의 원인은 부트스트랩을 사용하기 위해서는 jQuery가 필수적으로 요구되는데, jQuery가 로드되지 않아서 생기는 에러이다. 즉, html 페이지에 link 혹은 script src로 스크립트들을 로드할때, jQuery라이브러리를 먼저 임포트하고 그 밑에 부트스트랩을 임포트 시키면 해결되는 문제이다. 즉, 해당 에러는 반대로 부트스트랩 라이브러리를 jQuery보다 위에 임포트해놔서 생기는 에러인 것이다.

 

1
2
3
4
5
6
7
8
OK=>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
 
Failed=>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
 
cs

 

posted by 여성게
:

Java - lambda(람다) 간단한 사용법 !


람다란 무엇일까? 람다(lambda)란 간단하게 표현하면 메서드에 전달 가능한 함수형 인터페이스의 구현체이다.

그럼 여기서 함수형 인터페이스란 무엇인가? 함수형 인터페이스는 하나의 메소드만 선언되어 있는 인터페이스를 함수형 인터페이스라고 부른다.

이것의 예제 소스코드를 보면,

1
2
3
4
@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}
cs

이것은 java.util.concurrent의 Callable 인터페이스이다. 이러한 인터페이스를 함수형 인터페이스라고 부른다.

(@FunctionalInterface 어노테이션을 붙이면 이 인터페이스가 함수형 인터페이스 조건에 어긋나면 예외가 발생)


여기서 이런 생각이 들 수 있다. 하나의 함수만 선언된 인터페이스가 무슨 소용인가..? 함수형 인터페이스는 무조건 우리가 매개변수로 전달한 람다식만 사용가능한 인터페이스인가? 아니다! 자바 1.8의 혁신적인 변화중 람다,스트림등등 이외에도 인터페이스 디폴트 메소드가 존재한다.


1
2
3
4
5
6
7
8
9
10
11
12
@FunctionalInterface
public interface Function<T, R> {
 
    R apply(T t);
 
    default <V> Function<V, R> compose(Function<super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
 
    ....
}
cs


위는 자바의 Function 인터페이스 코드의 일부를 발췌한 것이다. 위의 코드를 보면 이상한 생각이 들수도 있다.

분명 함수형 인터페이스는 하나의 메소드 선언만 갖는다고 했는데, 위의 인터페이스는 default라는 선언이 된 메소드를 갖고 있다.


사실 위에서 정의한 바를 정확히 얘기하면 함수형 인터페이스란 구현 해야될 메소드가 하나인 인터페이스를 말하는 것이다. 위의 default메소드의 기능이란 무엇인가 하면, 이전 인터페이스는 메소드 선언만 가능했다. 그런데 지금은 모든 인터페이스 구현체가 동일한 기능을 쓴다면 계속 반복해서 오버라이드하는 것이 아닌, 인터페이스에 구현 메소드를 아예 선언해 버리는 기능인 것이다. 이것이 default 메소드의 기능이다. 


그렇다면 위의 Function 인터페이스는 사용할때 구현해야하는 메소드를 하나만 갖는 함수형 인터페이스인 것이다.



Lambda(람다)에는 아주 다양한 함수형 인터페이스가 있다. 여기서는 몇가지의 함수형 인터페이스를 이용하여

사용법을 다루어볼 것이다.




BiConsumer<T,U>

Represents an operation that accepts two input arguments and returns no result.

BiFunction<T,U,R>

Represents a function that accepts two arguments and produces a result.

BinaryOperator<T>

Represents an operation upon two operands of the same type, producing a result of the same type as the operands.

BiPredicate<T,U>

Represents a predicate (boolean-valued function) of two arguments.

BooleanSupplier

Represents a supplier of boolean-valued results.

Consumer<T>

Represents an operation that accepts a single input argument and returns no result.

DoubleBinaryOperator

Represents an operation upon two double-valued operands and producing a double-valued result.

DoubleConsumer

Represents an operation that accepts a single double-valued argument and returns no result.

DoubleFunction<R>

Represents a function that accepts a double-valued argument and produces a result.

DoublePredicate

Represents a predicate (boolean-valued function) of one double-valued argument.

DoubleSupplier

Represents a supplier of double-valued results.

DoubleToIntFunction

Represents a function that accepts a double-valued argument and produces an int-valued result.

DoubleToLongFunction

Represents a function that accepts a double-valued argument and produces a long-valued result.

DoubleUnaryOperator

Represents an operation on a single double-valued operand that produces a double-valued result.

Function<T,R>

Represents a function that accepts one argument and produces a result.

IntBinaryOperator

Represents an operation upon two int-valued operands and producing an int-valued result.

IntConsumer

Represents an operation that accepts a single int-valued argument and returns no result.

IntFunction<R>

Represents a function that accepts an int-valued argument and produces a result.

IntPredicate

Represents a predicate (boolean-valued function) of one int-valued argument.

IntSupplier

Represents a supplier of int-valued results.

IntToDoubleFunction

Represents a function that accepts an int-valued argument and produces a double-valued result.

IntToLongFunction

Represents a function that accepts an int-valued argument and produces a long-valued result.

IntUnaryOperator

Represents an operation on a single int-valued operand that produces an int-valued result.

LongBinaryOperator

Represents an operation upon two long-valued operands and producing a long-valued result.

LongConsumer

Represents an operation that accepts a single long-valued argument and returns no result.

LongFunction<R>

Represents a function that accepts a long-valued argument and produces a result.

LongPredicate

Represents a predicate (boolean-valued function) of one long-valued argument.

LongSupplier

Represents a supplier of long-valued results.

LongToDoubleFunction

Represents a function that accepts a long-valued argument and produces a double-valued result.

LongToIntFunction

Represents a function that accepts a long-valued argument and produces an int-valued result.

LongUnaryOperator

Represents an operation on a single long-valued operand that produces a long-valued result.

ObjDoubleConsumer<T>

Represents an operation that accepts an object-valued and a double-valued argument, and returns no result.

ObjIntConsumer<T>

Represents an operation that accepts an object-valued and a int-valued argument, and returns no result.

ObjLongConsumer<T>

Represents an operation that accepts an object-valued and a long-valued argument, and returns no result.

Predicate<T>

Represents a predicate (boolean-valued function) of one argument.

Supplier<T>

Represents a supplier of results.

ToDoubleBiFunction<T,U>

Represents a function that accepts two arguments and produces a double-valued result.

ToDoubleFunction<T>

Represents a function that produces a double-valued result.

ToIntBiFunction<T,U>

Represents a function that accepts two arguments and produces an int-valued result.

ToIntFunction<T>

Represents a function that produces an int-valued result.

ToLongBiFunction<T,U>

Represents a function that accepts two arguments and produces a long-valued result.

ToLongFunction<T>

Represents a function that produces a long-valued result.

UnaryOperator<T>

Represents an operation on a single operand that produces a result of the same type as its operand.


위의 표는 자바 api가 제공하는 많은 함수형 인터페이스이다. 사실 위의 표 이외에도 Callable,Runnable 등의 많은 함수형 인터페이스가 존재한다.


이중 몇가지만 예제로 구현해 보았다.



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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
public class LambdaTest {
 
    public static void main(String[] args) {
        /*
         * Predicate
         * 문자열을 받아서 해당 문자열이 빈 문자열인지를 반환
         */
        String str = "asd";
        System.out.println(lambdaIsEqual(p->p.isEmpty(), str));
        
        /*
         * Cunsumer
         * 인수를 각각 +1 해줌.
         */
        List<Integer> list = Arrays.asList(1,2,3,4,5);
        lambdaProcessParam( (Integer c)->{
                                        list.set(c-1,c+1);
                                        } , list);
        System.out.println(Arrays.toString(list.toArray()));
        
        /*
         * Function
         * 문자열 리스트를 받아서 각각의 문자열의 길이를 담은 리스트로 반환
         */
        List<String> list2 = Arrays.asList("ab","cds","ewwqd","a");
        List<Integer> result = lambdaConvertParam(list2, f->f.length());
        System.out.println(Arrays.toString(result.toArray()));
        
        /*
         * Supplier
         * 랜덤한 숫자를 반환한다.
         */
        System.out.println(lambdaSupplier(()->{
            return (int)(Math.random()*10)+1;
        }));
        
        /*
         * UnaryOperator
         * 숫자 리스트를 받아 각 숫자에 +2한 값을 담은 리스트로 반환
         */
        List<Integer> ele = Arrays.asList(1,2,3,4,5);
        List<Integer> result2 = lambdaUnaryOper(ele, uo->uo+2);
        System.out.println(Arrays.toString(result2.toArray()));
        
        /*
         * BinaryOperator
         * 두개의 문자열을 받아서 공백을 기준으로 두개의 문자열을 합하여 반환
         */
        String str1 = "yeoseong";
        String str2 = "yoon";
        System.out.println(lambdaBinaryOper(str1, str2, (bo1,bo2)->bo1+" "+bo2));
        
        /*
         * 하나의 정수와 문자열을 입력받아 정수와 문자열의 길이가 같은지 검사
         */
        System.out.println(lambdaBiPred(4"yoon", (bp1,bp2)->bp1 == bp2.length()));
        
        /*
         * 문자열 리스트에 인덱스한 숫자 번호를 붙여준다.
         */
        List<String> list3 = Arrays.asList("a","b","c","d");
        List<String> result4 = new ArrayList<>();
        lambdaBiConsumer(result4,list3, ()->(int)(Math.random()*10)+1, (bc1,bc2)->{
            bc1 = bc1.concat("-"+bc2+"");
            result4.add(bc1);
        });
        System.out.println(Arrays.toString(result4.toArray()));
        
        /*
         * lambda를 포함하면서 길이가 5이상인 문자열인가?
         */
        System.out.println(lambdaPredAnd("It's lambda", (String p1)->p1.contains("lambda"), (String p2)->p2.length()>5));
        
        /*
         * 숫자를 입력받아서 +1한 후에 *2를 수행한 숫자를 반환
         */
        System.out.println(lambdaFuncAndThen(1, f1->f1+1, f2->f2*2));
    }
    
    /**
     * boolean Predicate<T>
     * 하나의 인수를 받아서 적절한 로직을 처리한 후에 boolean을 반환한다.
     */
    public static <T> boolean lambdaIsEqual(Predicate<T> predicate,T t) {
        return predicate.test(t);
    }
    
    /**
     * void Consumer<T>
     * 하나의 인수를 받아서 인수를 소모한후 void를 반환.
     */
    public static <T> void lambdaProcessParam(Consumer<T> consumer,List<T> list) {
        list.forEach(e->consumer.accept(e));
    }
    
    /**
     * R Function<T>
     * <T,R>의 인수를 받아서 T타입을 R타입으로 변환후 반환한다.
     */
    public static <T,R> List<R> lambdaConvertParam(List<T> list, Function<T, R> f) {
        List<R> result = new ArrayList<>();
        list.forEach(c->{
            result.add(f.apply(c));
        });
        return result;
    }
    
    /**
     * T Supplier
     * 매개변수는 없고 T타입을 반환한다.
     * @return 
     */
    public static <T> T lambdaSupplier(Supplier<T> s) {
        return s.get();
    }
    /**
     * T타입의 매개변수를 받아 같은 타입의 T타입의 값을 반환
     */
    public static <T> List<T> lambdaUnaryOper(List<T> list,UnaryOperator<T> uo){
        List<T> result = new ArrayList<>();
        list.forEach(c->{
            result.add(uo.apply(c));
        });
        return result;
    }
    
    /**
     * T타입의 매개변수를 2개 받아서, T타입의 값으로 반환
     */
    public static <T> T lambdaBinaryOper(T a,T b,BinaryOperator<T> bo) {
        return bo.apply(a, b);
    }
    
    /**
     * T,R타입의 매개변수를 받아서 boolean 값을 반환
     */
    public static <T,R> boolean lambdaBiPred(T t,R r,BiPredicate<T, R> bp) {
        return bp.test(t, r);
    }
    
    /**
     * T,R타입의 매개변수를 받아서 적절한 처리를 한다. void 반환
     */
    public static <T,R> void lambdaBiConsumer(List<T> result,List<T> t , Supplier<R> r , BiConsumer<T, R> bc) {
        t.forEach(c->{
            bc.accept(c,r.get());
        });
    }
    
    /**
     * Predicate and/or/negate
     * 두개 이상의 Predicate를 and로 묶을 수 있다.
     */
    public static <T> boolean lambdaPredAnd(T t,Predicate<T> p1,Predicate<T> p2) {
        return p1.and(p2).test(t);
    }
    
    /**
     * Function andThen,compose
     * andThen a.andThen(b) a를 먼저 수행한 결과를 b의 함수의 입력으로 가져간다.
     * compose a.compose(b) b를 먼저 수행한 결과를 a의 함수의 입력으로 가져간다.
     */
    public static <T> T lambdaFuncAndThen(T t, Function<T, T> f1, Function<T, T> f2) {
        return f1.andThen(f2).apply(t);
    }
}
 
cs




이제는 람다에서 알아야할 몇가지 정보가 있다. 

1) 예외, 람다, 함수형 인터페이스의 관계 - 함수형 인터페이스는 확인된 예외를 던지는 동작을 허용하지 않는다. 즉, 예외를 던지는 람다 표현식을 만드려면 확인된 예외를 선언하는 함수형 인터페이스를 직접 정의하거나 람다를 try/catch 블록으로 감싸야한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
@FunctionalInterface
public interface Lamda{
    T process(T t) throws Exception;
}
 
 
Function<BufferedReader, String> f = 
    (BufferedReader b)-> {
        try{
            return b.readLine();
        }catch(IOException e){
            throw new RuntimeException(e);
        }
    };
cs


2)형식 추론 - 위의 예제를 보면 매개변수로 전달하는 람다의 인자에는 인자의 형식을 캐스팅하지 않는다. 인자의 형식은 선언된 메소드에서 내부적으로 추론해낸다.


3)람다의 조합 - Predicate(and,or,negate),Function(andThen,compose).... 람다식끼리 조합해서 더 제한적인 조건 혹은, 더 많은 처리를 파이프라인처럼

묶을 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    /**
     * Predicate and/or/negate
     * 두개 이상의 Predicate를 and로 묶을 수 있다.
     */
    public static <T> boolean lambdaPredAnd(T t,Predicate<T> p1,Predicate<T> p2) {
        return p1.and(p2).test(t);
    }
    
    /**
     * Function andThen,compose
     * andThen a.andThen(b) a를 먼저 수행한 결과를 b의 함수의 입력으로 가져간다.
     * compose a.compose(b) b를 먼저 수행한 결과를 a의 함수의 입력으로 가져간다.
     */
    public static <T> T lambdaFuncAndThen(T t, Function<T, T> f1, Function<T, T> f2) {
        return f1.andThen(f2).apply(t);
    }
cs


위의 예제에도 나왔지만 다시 한번 설명하기 위해 발췌했다. 이렇게 람다식끼리 조건부로 연결가능하다. 


이상 자바8의 람다에 대한 설명이었다. 사실 람다에 대해 설명하지 못한 것이 훨씬 많다. 내가 설명한 것은 람다의 일부일 것이다. 하지만 이번 포스팅에서는

람다란 무엇이고 어떤 식으로 접근하여 어떻게 사용할 것인가를 설명하기 위한 포스팅이었기에 이 글을 읽고 간단한 사용법을 익혀

나중에 응용해 나갔으면 하는 생각에 더 복잡하고 많은 설명을 하지 않았다.

posted by 여성게
:

Java - Collections.rotate() 란?



만약 List 객체에 [1,2,3,4,5] 요소들이 들어있다고 생각해보자. 

여기에서 Collections.rotate(list,2) 메소드를 호출한다면 맨뒤의 요소를 하나씩 두번꺼내서 맨앞 요소자리에 넣고

다른 요소들은 뒤로 한칸씩밀리게 되는 것이다.


이것을 Step으로 표현하면


1
2
3
4
5
6
7
8
9
10
@Test
public void CollectionsRotate(){
    List<Integer> list = new ArrayList<>();
    list.addAll(Arrays.asList(new Integer[] {1,2,3,4,5}));
    
    Collections.rotate(list, 1);
    System.out.println(Arrays.toString(list.toArray()));
    Collections.rotate(list, 1);
    System.out.println(Arrays.toString(list.toArray()));
}
cs




Collections.rotate(list,2)를 step을 두단계로 나누어서 결과를 출력하기 위해 Collections.rotate(list,1)을 두번 출력했다.

이것들의 결과는

>[5,1,2,3,4]

>[4,5,1,2,3]


이라는 결과를 출력하게된다. 이것은 쉽게 말하면 마지막 요소를 꺼내서 맨앞 요소자리로 넣는 것이고, 나머지 요소들은 방금꺼낸 요소의

자리까지 한칸 밀리게 되는 것이다.

posted by 여성게
: