[ GC : Garbage collector ]

JVM 메모리는 크게 class / stack / heap 영역으로 나누어지며,

GC 는 heap 영역을 대상으로 수행된다.

 

Heap 영역은 

Young / Old / Perm 영역으로 나눌 수 있으며 (java 8부터 Perm 영역은 제거되었음)

Young 영역은 

Eden , survivor 0, survivor 1 로 나눌 수 있다.

 

최초 객체 생성시 Eden 영역에 할당되며,

Eden 영역이 다 찼을 경우 reachable (접근 가능한 상태) 한 객체는 survivor 0로 옮기고 Eden 영역을 정리한다 (minor GC)

반복적으로 Eden 영역을 정리하다 survivor 0 이 다 찬 경우, reachable 한 객체를 survivor 1 로 옮긴 후 survivor 0 을 비워준다.

객체의 age 가 특정 임계점 도달시 Old 영역으로 객체를 옮겨준다 (promote)

위 과정을 반복하다 Old 영역도 다 차게되면 Full GC (major GC) 가 발생한다.

 

GC 수행시 아래 작업을 공통적으로 수행

Mark and Sweep : Mark(사용되는 메모리와 사용되지 않는 메모리 구분), Sweep(사용되지 않는 메모리 해제)

Stop the world : GC 수행하는 쓰레드를 제외한 모든 쓰레드 작업 중단

 

[ GC 알고리즘 종류 ]

1. Serial GC : 싱글쓰레드로 GC 수행

2. Parallel GC : Young 영역 GC 를 멀티쓰레드로 수행 (java 8)

3. Parallel Old GC : Young , Old 영역 모두 GC를 멀티쓰레드로 수행 (java 8)

3. CMS GC : 

4. G1 GC : Heap 을 일정한 크기의 region 으로 나눔. (java 9)

 

 

 

https://mangkyu.tistory.com/118

https://velog.io/@injoon2019/CS-%EC%A0%95%EB%A6%AC-GC-Garbage-Collector

 

 

 

 

 

반응형


자바 직렬화

https://devlog-wjdrbs96.tistory.com/268
https://findmypiece.tistory.com/m/166


반응형

메소드 안에서 다음과 같이 쌩뚱맞은 중괄호 블락이 보인다면,

이는 변수의 유효 범위(scope)를 제한하기 위함이다.

 

1
2
3
4
5
6
7
8
9
public void methodA(){
    {
        int a = 0;
    }
    
    int a = 2;
 
    return b;
}
cs

3번째 라인의 a 변수는 { } 안에서만 유효한 지역변수이며,

위와 같이 a 변수를 6번라인에서 선언해도 문제 되지 않는다.

 

* 변수 유효범위를 제한하여 코딩할 때 사용하나, 드물게 사용된다. 

 

반응형

URL에 직접 접근할 수 없도록 처리

: header 에서 referer 정보를 확인하여 직접 접근 여부 판별이 가능하다.

referer 는 변조가 가능하여 직접 접근을 완벽히 막는 방법이라고 할 순 없을 듯 하나, 보통은 이정도만 처리해도 될 듯 하다.

 

 

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
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String contextPath = request.getContextPath();
    
    if(handler instanceof HandlerMethod) {
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        
        RefererUncheck lefererUncheck = handlerMethod.getMethodAnnotation(RefererUncheck.class);
        
        logger.info("Header Check. (getRemoteAddr=" + StringUtils.trimToNull(request.getRemoteAddr()) + ")");
        logger.info("Header Check. (getRequestUrl=" + StringUtils.trimToNull(request.getRequestURI().substring(contextPath.length())));
        try {
            // URL 접속정보
            String referer = StringUtils.trimToNull(request.getHeader("Referer"));
            logger.info("referer : " + referer);
            // 리퍼럴 어노테이션이 없고, ajax 통신이 아닌경우만 체크
            if (lefererUncheck == null && !this.isAjaxRequest(request)) {
                logger.debug("Referer Check Start! (Referer=" + StringUtils.trimToNull(request.getHeader("Referer")) + ")");
                logger.debug("Referer Check success! (URI=" + request.getRequestURI() + ")");
                if (StringUtils.isEmpty(referer)) {
                    throw new CustomException("error.referer");
                }
            }
            
        } catch (CustomException be) {
            request.getRequestDispatcher(contextPath+ "/cmm/error.jsp").forward(request, response);
            return false;
        } catch (Exception e) {
            logger.error(e.getMessage());
            return false;
        }
    }
    
    return true;
}
private boolean isAjaxRequest(HttpServletRequest request) {
    final String header = request.getHeader(Constants.AJAX_HEADER);
    if( header != null && header.equals("true") ) {
        return true;
    }
    return false;
}
 
cs

 

referer Mozilla doc

 

 

반응형

자바 8부터 람다와 함께 사용되는 스트림.

아직 자바 8 환경에서 개발해 볼 경험이 없었지만 정리를 미리 해둔다.

 

스트림은 아래와 같은 파이프라인으로 작성한다.

객체집합.스트림생성().중개연산().최종연산() 

1. 스트림 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static void construct() {
  //1. Collection으로 스트림 생성
  //collection.stream();
  List<String> names = Arrays.asList("pyo""sue");
  names.stream();
  
  //2. 배열로 스트림 생성
  //array.stream();
  int[] arr = {123};
  Arrays.stream(arr);  
  
  //3. 스트림 직접 생성
  //Stream.of(~);
  Stream<Integer> stream = Stream.of(1,2,3);   
}
cs

 

2. 중개연산

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
private static List<String> names = Arrays.asList("pyo""sue""aron""ballack""pyo");
 
//2. 중개연산
private static void middle() {
  //2-1. Filter : 특정 조건에 부합하는 데이터만 뽑을 때 사용
  long cnt = names.stream().filter(new Predicate<String>() {
     @Override
     public boolean test(String t) {
        return t.contains("y");
     }
  }).count();
  System.out.print("result of counting after filtering : " + cnt);
  System.out.println("\n-----------------------------");
  
  //2-2. Map : 특정 연산을 할 때 사용
  names.stream().map(new Function<StringString>() {
     @Override
     public String apply(String t) {
        return t.concat("@gmail.com");
     }
  }).forEach(x -> System.out.print(x+" "));
  System.out.println("\n-----------------------------");
  
  //2-3. sort : 정렬
  names.stream().sorted((x1, x2)->x1.compareTo(x2)).forEach(x->System.out.print(x+" "));
  System.out.println("\n-----------------------------");
  
  //2-4. limit : 출력 수 제한
  names.stream().limit(2).forEach(x->System.out.print(x+" "));
  System.out.println("\n-----------------------------");
  
  //2-5. distinct : 중복 제거
  names.stream().distinct().forEach(x->System.out.print(x+" "));
  System.out.println("\n-----------------------------");
  
}
 
cs

 

결과

result of counting after filtering : 2
-----------------------------
pyo@gmail.com sue@gmail.com aron@gmail.com ballack@gmail.com pyo@gmail.com
-----------------------------
aron ballack pyo pyo sue
-----------------------------
pyo sue
-----------------------------
pyo sue aron ballack
-----------------------------

3. 최종연산

1
2
3
4
5
6
7
8
private static List<String> names = Arrays.asList("pyo""sue""aron""ballack""pyo");
 
//3. 최종연산
private static void finalOperate() {
  names.stream().forEach(x->System.out.print(x+" "));
  names.stream().count();
  //names.stream().max();
}
cs

 

 

람다보단 개념이 단순해 보이나

적응되는데 시간이 필요할 듯 하다.

 

참고

반응형

 

 

[ Error vs Exception ]

Error: An Error indicates serious problem that a reasonable application should not try to catch.

> 애플리케이션이 잡지 말아야 할 심각한 에러(핸들링이 어려운 에러)

 

Exception: Exception indicates conditions that a reasonable application might try to catch.

> 애플리케이션이 잡을 수 있는 에러(핸들링이 할 수 있는 에러)

 

 

[ ClassNotFoundError vs NoDefClassFoundError ]

ClassNotFoundException is an exception that occurs when you try to load a class at run time using Class.forName() or loadClass() methods and mentioned classes are not found in the classpath.

> 런타임에 Class.forName() 혹은 loadClass() 메소드를 사용했고 refrection에 사용된 class가 classpath에 없는 경우 발생

 

NoClassDefFoundError is an error that occurs when a particular class is present at compile time, but was missing at run time.

> 특정 클래스가 컴파일시엔 존재했으나 런타임 때 찾을 수 없는 경우

위와 관련된 경험: https://developyo.tistory.com/241?category=747217

 

 

[Checked Exception vs Unchecked Exception ]

Checked Exception : Java forces you to handle these error scenarios in some manner in your application code. 

> java 가 핸들링을 강요하는 exception (ex: sqlException, IOException)

 

Unchecked Exception : A method is not forced by compiler to declare the unchecked exceptions thrown by its implementation. Generally, such methods almost always do not declare them, as well.

Spring Transaction only supervise Unchecked Exceptions.

> 핸들링을 강요하지 않는 exception (ex: NullPointerException)

 

 

참고)

error vs exception

https://www.geeksforgeeks.org/exceptions-in-java/

ClassNotFoundError vs NoDefClassFoundError

https://dzone.com/articles/java-classnotfoundexception-vs-noclassdeffounderro

CheckedException vs Unchecked Exception

https://www.geeksforgeeks.org/checked-vs-unchecked-exceptions-in-java/

 

반응형

아래는 1년전 쯤 개발했던 httpUrlConnection 모듈이다

https://developyo.tistory.com/10?category=688588

 

시간이 지날 수록 소스는 누더기가 되어가고..

디자인패턴을 공부하고, 객체지향스러운 개발을 위해 이책 저책(클린코드 도서 등) 읽고나니

위 코드가 리펙토링이 필요하다는 것을 느껴 리펙토링을 해보았다.

 

상황]

1. 연동되는 서버가 계속해서 늘어났다.

2. 각각의 연동 서버에 맞춰야 되는 예외상황들이 늘어났다. (ex: timeout, charset, retry, responsecode..)

3. 예외상황들을 처리 하기 위한 파라미터들을 받기 위해 메소드의 인자값이 늘어났다.

4. 오버로딩되는 메소드가 늘어났다.

 

방향]

1. 객체를 받도록 처리하자. 

: 파라미터는 적을 수록 좋고,  파라미터가 너무 많이지면 객체를 파라미터로 받도록 소스를 리펙토링 해야한다.

2. boolean 값을 받는 메소드 1개는 두개의 메소드 따로 만들자. (도서 클린코드 참고)

: requestApi(boolean isJsonFormat, ...) 와 같이 boolean 값을 파라미터로 받은 후 분기처리 처리하지 말고

requestApiJsonFormat(...), requestApi(...) 와 같이 두개의 메소드로 분리해야 한다. (도서 클린코드 참고)

3. 필수값과 optional 값을 구분하기 위해 builder 패턴을 적용하여 객체를 설계해보자.

4. 전처리 후처리 로깅을 proxy 패턴을 적용하여 처리해보자. 

5. junit 을 사용하여 테스트해보자. (junit 관련 포스팅1, junit 관련 포스팅2)

 

1. RequestForm

httpUrlConnection 호출에 필요한 파라미터 정보 객체

빌더패턴을 적용하여 필수 파라미터(목적지주소, http method)는 생성자로,

그외 optional 파라미터는 setter 로 받도록 했다.

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
package com.jpp.web.util.HttpUtil;
 
import java.util.Map;
 
public class RequestForm {
   
   private final int tryCount;
   private final int connectionTimeOut;
   private final int readTimeOut;
   private final String httpMethod;
   private final String url;
   private final Map<String, Object> header;
   private final int httpStatus;
   private final String requestCharset;
   private final String responseCharset;
   
   public static class Builder {
      private String httpMethod; //required
      private String url;        //required
      private int tryCount = 1;  //optional
      private int connectionTimeOut = 1000;  //optional
      private int readTimeOut = 1000;     //optional
      private Map<String, Object> header; //optional
      private int httpStatus = 0;    //optional
      private String requestCharset = "UTF-8";
      private String responseCharset = "UTF-8";
      
      public Builder(String httpMethod, String url) {
         this.httpMethod = httpMethod;
         this.url = url;
      }
      
      public Builder setTryCount(int tryCount) {
         this.tryCount = tryCount<1?1:tryCount;
         return this;
      }
      
      public Builder setConnectionTimeOut(int connectionTimeOut) {
         this.connectionTimeOut = connectionTimeOut<100?1000:connectionTimeOut;
         return this;
      }
      
      public Builder setReadTimeOut(int readTimeOut) {
         this.readTimeOut =  readTimeOut<100?1000:readTimeOut;
         return this;
      }
      
      public Builder setHeader(Map<String, Object> header) {
         this.header = header;
         return this;
      }
      
      public Builder setExpectedHttpStatus(int httpStatus) {
         this.httpStatus = httpStatus;
         return this;
      }
      
      public Builder setRequestCharset(String requestCharset) {
         this.requestCharset = requestCharset;
         return this;
      }
      
      public Builder setResponseCharset(String responseCharset) {
         this.responseCharset = responseCharset;
         return this;
      }
      
      public RequestForm build() {
         return new RequestForm(this);
      }
   }
   
   public RequestForm(Builder builder) {
      this.httpMethod = builder.httpMethod;
      this.url = builder.url;
      this.tryCount = builder.tryCount;
      this.connectionTimeOut = builder.connectionTimeOut;
      this.readTimeOut = builder.readTimeOut;
      this.header = builder.header;
      this.httpStatus = builder.httpStatus;
      this.requestCharset = builder.requestCharset;
      this.responseCharset = builder.responseCharset;
   }
   
   public int getTryCount() {
      return tryCount;
   }
 
   public int getConnectionTimeOut() {
      return connectionTimeOut;
   }
 
   public int getReadTimeOut() {
      return readTimeOut;
   }
 
   public String getHttpMethod() {
      return httpMethod;
   }
 
   public String getUrl() {
      return url;
   }
   
   public Map<String, Object> getHeader(){
      return header;
   }
   
   public int getHttpStatus() {
      return httpStatus;
   }
 
   public String getRequestCharset() {
      return requestCharset;
   }
 
   public String getResponseCharset() {
      return responseCharset;
   }
   
}
 
cs

 

 

2. CustomHttpUrlConnection

java.net.HttpUrlConnection 객체를 사용하여 외부 서버 api를 호출하는 역할을 하는 클래스

1) 요청정보를 담고있는 RequestForm객체를 파라미터로 받도록 했다.

2) jsonFormat(body로 호출) 여부인 boolean 값을 파라미터로 받아 내부에서 분기처리하는게 아닌, 별도의 메소드로 각각 분리했다.

메소드를 작은 단위로 쪼개면 코드 중복이 많이 줄어들 것 같은데 나중에..

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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
package com.jpp.web.util.HttpUtil;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
 
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import com.google.gson.Gson;
import com.jpp.web.comm.CustomException;
import com.jpp.web.constants.Constants;
import com.jpp.web.constants.ConstantsEnum;
 
public class CustomHttpUrlConnection {
   
   private static final Logger logger = LoggerFactory.getLogger(CustomHttpUrlConnection.class);
   private static final String UTF_8 = "UTF-8";
 
   private int responseCode = 0;
   private long startTime = 0;
 
   private RequestForm reqForm;
   private Map<String, Object> reqParams;
   
   public CustomHttpUrlConnection(RequestForm reqForm, Map<String, Object> reqParams){
      this.reqForm = reqForm;
      this.reqParams = reqParams;
   }
   
   public int getResponseCode() {
      return responseCode;
   }
   
   public long getStartTime() {
      return startTime;
   }
   
   public String requestApi() {
      
      this.startTime = System.currentTimeMillis();
      
      String httpMethod = reqForm.getHttpMethod();  
      String surl = reqForm.getUrl();               
      int tryCnt = reqForm.getTryCount();   
      int readTimeOut = reqForm.getReadTimeOut(); 
      int connectionTimeOut = reqForm.getConnectionTimeOut();
      Map<String, Object> header = reqForm.getHeader();
      int expectedHttpStatus = reqForm.getHttpStatus();
      String requestCharset = reqForm.getRequestCharset();
      String responseCharset = reqForm.getResponseCharset();
      
      String reqCharset = requestCharset==null||requestCharset.isEmpty()?UTF_8:requestCharset;
      String resCharset = responseCharset==null||responseCharset.isEmpty()?UTF_8:responseCharset;
             
      URL url = null;
      HttpURLConnection conn = null;
      BufferedReader br = null;
      JSONObject jobj = null;
      String postParams = "";
      String errMsg = "";
      String returnText = "";
 
      
      for(int i=0; i < tryCnt; i++){
          
         try {
             
            if(httpMethod.equalsIgnoreCase(Constants.POST) || httpMethod.equalsIgnoreCase(Constants.DELETE)){
               url = new URL(surl);
            } else if(httpMethod.equalsIgnoreCase(Constants.GET)){
               url = new URL(surl + ((reqParams!=null)?"?"+makeUrlEncodedParams(reqParams, reqCharset):""));
            }
            
            conn = (HttpURLConnection) url.openConnection();
            
            if(header != null){
                for(String key : header.keySet()) {
                    conn.setRequestProperty(key, header.get(key)!=null?header.get(key).toString():"");
                }
            }
             
            conn.setRequestMethod(httpMethod);
            conn.setConnectTimeout(connectionTimeOut);
            conn.setReadTimeout(readTimeOut);
            conn.setDoOutput(true);
            
            if(httpMethod.equalsIgnoreCase(Constants.POST) || httpMethod.equalsIgnoreCase(Constants.DELETE)){
               if(reqParams != null){
                  postParams = makeUrlEncodedParams(reqParams, reqCharset);
                  conn.getOutputStream().write(postParams.getBytes(UTF_8));
                  conn.getOutputStream().flush();
               }
            }
            
            this.responseCode = conn.getResponseCode();
            
            if(expectedHttpStatus > 0){
               if(expectedHttpStatus!=conn.getResponseCode()){
                  throw new CustomException("successCode : {" + expectedHttpStatus + "}" + " , responseCode : {" + this.responseCode + "}", ConstantsEnum.API_RESULT.E_NETWORK.getCode());
               }
            }
            
            br = new BufferedReader(new InputStreamReader(conn.getInputStream(),resCharset));
            
            StringBuffer sb = null;
            sb = new StringBuffer();
            
            String jsonData = "";
            while((jsonData = br.readLine()) != null){
               sb.append(jsonData);
            }
            returnText = sb.toString();
            
            try{
               jobj = new JSONObject(returnText);
            } catch (JSONException e){
               throw new CustomException();
            }
            
            break;
            
         } catch (SocketTimeoutException se){
            logger.error("connection fail : " + se);
             errMsg = se.getMessage();
         } catch (CustomException e){
            logger.error("response fail : " + e);
             errMsg = e.getMessage();
         } catch (Exception e){
            throw new CustomException(e.getMessage().toString(), ConstantsEnum.API_RESULT.E_NETWORK.getCode());
         } finally {
            try {
               if (br != null) br.close();
            } catch(Exception e){
               logger.warn("finally..br.close()", e);
            }
            br = null;
            try {
               if(conn!=null) {
                  conn.disconnect();
               }
            } catch(Exception e){
               logger.warn("finally..conn.disconnect()", e);
            }
            conn = null;
         }
      }
      
      if(jobj!=null){
         return jobj.toString();
      } else {
         throw new CustomException(errMsg, ConstantsEnum.API_RESULT.E_NETWORK.getCode());
      }
   }
   
   
   public String requestApiWithJsonForm() {
      
      this.startTime = System.currentTimeMillis();
      
      String httpMethod = reqForm.getHttpMethod();
      int tryCnt = reqForm.getTryCount();
      int readTimeOut = reqForm.getReadTimeOut();
      int connectionTimeOut = reqForm.getConnectionTimeOut();
      String surl = reqForm.getUrl();
      Map<String, Object> header = reqForm.getHeader();
      int expectedHttpStatus = reqForm.getHttpStatus();
      String responseCharset = reqForm.getResponseCharset();
      
      String resCharset = responseCharset==null||responseCharset.isEmpty()?UTF_8:responseCharset;
      
      URL url = null;
      HttpURLConnection conn = null;
      BufferedReader br = null;
      JSONObject jobj = null;
      String postParams = "";
      String errMsg = "";
      String returnText = "";
       
      for(int i=0; i < (tryCnt<1?1:tryCnt); i++){
          
         try {
             
            url = new URL(surl);
            
            conn = (HttpURLConnection) url.openConnection();
            
            if(header != null){
                for(String key : header.keySet()) {
                    conn.setRequestProperty(key, header.get(key)!=null?header.get(key).toString():"");
                }
            }
             
            conn.setRequestProperty("Content-Type""application/json");
            conn.setRequestMethod(httpMethod);
            conn.setConnectTimeout(connectionTimeOut);
            conn.setReadTimeout(readTimeOut);
            conn.setDoOutput(true);
            
            if(reqParams != null){
                postParams = makeJsonParams(reqParams);
                conn.getOutputStream().write(postParams.getBytes(UTF_8));
                conn.getOutputStream().flush();
            }
            
            this.responseCode = conn.getResponseCode();
            
            if(expectedHttpStatus != 0){
               if(expectedHttpStatus!=conn.getResponseCode()){
                  throw new CustomException("successCode : {" + expectedHttpStatus + "}" + " , responseCode : {" + this.responseCode + "}", ConstantsEnum.API_RESULT.E_NETWORK.getCode());
               }
            }
            
            br = new BufferedReader(new InputStreamReader(conn.getInputStream(), resCharset));
            
            StringBuffer sb = null;
            sb = new StringBuffer();
            
            String jsonData = "";
            while((jsonData = br.readLine()) != null){
               sb.append(jsonData);
            }
            returnText = sb.toString();
            
            try{
               jobj = new JSONObject(returnText);
            } catch (JSONException e){
               throw new CustomException();
            }
            
            break;
            
         } catch (SocketTimeoutException se){
            logger.error("connection fail : " + se);
             errMsg = se.getMessage();
         } catch (CustomException e){
            logger.error("response fail : " + e);
             errMsg = e.getMessage();
         } catch (Exception e){
            throw new CustomException(e.getMessage().toString(), ConstantsEnum.API_RESULT.E_NETWORK.getCode());
         } finally {
            try {
               if (br != null) br.close();
            } catch(Exception e){
               logger.warn("finally..br.close()", e);
            }
            br = null;
            try {
               if(conn!=null) {
                  conn.disconnect();
               }
            } catch(Exception e){
               logger.warn("finally..conn.disconnect()", e);
            }
            conn = null;
         }
      }
      
      if(jobj!=null){
         return jobj.toString();
      } else {
         throw new CustomException(errMsg, ConstantsEnum.API_RESULT.E_NETWORK.getCode());
      }
   }
    
   
   private String makeUrlEncodedParams(Map<String, Object> params, String charset) throws Exception{
      String param = "";
      StringBuffer sb = new StringBuffer();
      
      if(params != null){
         for ( String key : params.keySet() ){
             try {
                sb.append(key).append("=").append((params.get(key)==null?"":URLEncoder.encode(params.get(key).toString(), charset)).toString().trim()).append("&");
             } catch (UnsupportedEncodingException e) {
                logger.error("ex while encoding : {}", e.getMessage());
                throw e;
             }
         }
         param = sb.toString().substring(0, sb.toString().length()-1);
      }
      return param;
   }
  
   
   private String makeJsonParams(Map<String, Object> params){
      String json = "";
      if(params != null){
          json = new Gson().toJson(params);
      }
      return json;
   }
   
}
 
 
cs

 

 

3. HttpUtil

클라이언트에서 실제로 사용될 클래스

1) 프록시(proxy) 패턴을 적용하여 CustomHttpUrlConnection객체의 requestApi(), requestApiWithJsonForm() 메소드를 호출하기 전과 후에 logging 메소드가 호출되도록 했다

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
package com.jpp.web.util.HttpUtil;
 
import java.util.Map;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
public class HttpUtil {
    
   private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);
   
   private RequestForm reqForm;
   private Map<String, Object> reqParams;
   private CustomHttpUrlConnection httpUrlConnection;
   
   public HttpUtil(RequestForm reqForm, Map<String, Object> reqParams) {
      this.reqForm = reqForm;
      this.reqParams = reqParams;
      httpUrlConnection = new CustomHttpUrlConnection(reqForm, reqParams);
   }
   
   public String requestApi() {
      printBeforeLog();
      String resParams = httpUrlConnection.requestApi();
      printAfterLog(resParams);
      return resParams;
   }
   
   public String requestApiWithJsonForm() {
      printBeforeLog();
      String resParams = httpUrlConnection.requestApiWithJsonForm();
      printAfterLog(resParams);
      return resParams;
   }
   
   private void printBeforeLog() {
      logger.info("HTTPCONNECTION.REQUEST|URL:{}|IN_PARAMS:{}"
            , reqForm.getUrl(), reqParams);
   }
   
   private void printAfterLog(String resParams) {
      logger.info("HTTPCONNECTION.RESPONSE|URL:{}|TIME:{}|STATUS_CODE:{}|IN_PARAMS:{}|OUT_PARAMS:{}"
            , reqForm.getUrl()
            , System.currentTimeMillis() - httpUrlConnection.getStartTime()
            , httpUrlConnection.getResponseCode()<1?"":httpUrlConnection.getResponseCode()
            , reqParams
            , resParams);
   }
   
}
 
cs

 

4. test

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
package com.jpp.web.util.HttpUtil;
 
import java.util.HashMap;
import java.util.Map;
 
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
 
@RunWith(SpringRunner.class)
@SpringBootTest
public class HttpUtilTest {
   
   private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);
   
   @Test
   public void test() {
      RequestForm reqForm = new RequestForm.Builder("GET""127.0.0.1:8082/mobile/device")
            .setConnectionTimeOut(1000)
            .setReadTimeOut(1000)
            .setTryCount(3)
            .build();
      
      Map<String, Object> reqParams = new HashMap<String, Object>();
      reqParams.put("deviceType""1");
      reqParams.put("osVersion""10");
      
      String apiResult = new HttpUtil(reqForm, reqParams).requestApiWithJsonForm();
      logger.info(apiResult);
   }
 
   @Test
   public void test2() {
      RequestForm reqForm = new RequestForm.Builder("GET""127.0.0.1:8082/mobile/version")
            .setConnectionTimeOut(1000)
            .setReadTimeOut(1000)
            .setTryCount(3)
            .build();
      
      Map<String, Object> reqParams = new HashMap<String, Object>();
      
      String rs = new HttpUtil(reqForm, reqParams).requestApi();
      logger.info(rs);
   }
}
 
cs

 

소스리뷰를 받고싶다..

 

모든 코드는 제 github(https://github.com/develo-pyo/boot-mybatis)에 올려놓았습니다.

반응형

 

예제를 위한 클래스

1. ExampleSuperClass : 부모클래스

1
2
3
4
5
package Java.reflection;
 
public class ExampleSuperClass {
}
 
cs

 

2. ExampleClass : 예제 클래스

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
package Java.reflection;
 
public class ExampleClass extends ExampleSuperClass {
   
   private static final String CONSTANT_FIELD = "constant_field";
   public int i = 1;
   public String publicField = "publicStrField";
   private String privateField = "privateStrField";
   
   public ExampleClass() { 
   }
   
   public ExampleClass(Object a) throws NullPointerException {
   }
   
   private int privateMethod(Object a, int b) throws NullPointerException {
      if(a == null) {
         throw new NullPointerException();
      }
      return b;
   }
   
   public String addStrInt(String a, int b) {
      return a+b;
   }
}
cs

 

3. ExampleChildClass : 자식 클래스

1
2
3
4
package Java.reflection;
 
public class ExampleChildClass extends ExampleClass {       
}
cs

 

 

Java Reflection 의 사용 예

1. isInstance 사용법 

instanceof 와 같은 역할을 수행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package Java.reflection;
 
//https://gyrfalcon.tistory.com/entry/Java-Reflection
 
//instance of
public class Reflection1_instanceof {
   public static void main(String[] args) {
      
      //classA.isInstance(B);
      //B가 A의 인스턴스인지를 확인
      Class clazz = ExampleClass.class;
      
      boolean rs1 = clazz.isInstance(new ExampleSuperClass());
      System.out.println(rs1);
      boolean rs2 = clazz.isInstance(new ExampleClass());
      System.out.println(rs2);
      boolean rs3 = clazz.isInstance(new ExampleChildClass());
      System.out.println(rs3);
 
   }
}
 
cs

[결과]

false
true
true

 

2. 클래스에서 메소드 정보 가져오기

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
package Java.reflection;
 
import java.lang.reflect.Method;
 
//Method 정보 가져오기
public class Reflection2_FindMethod {
 
   public static void main(String[] args) throws Exception {
      
      //1. class 가져오기
      Class clazz = Class.forName("Java.reflection.ExampleClass");
      //1-1. class 가져오기(위와 같다)
      Class clazz1 = ExampleClass.class;
      
      //2. class 에서 method 가져오기
      Method methodArr[] = clazz.getDeclaredMethods();
      
      for(Method method : methodArr) {
         
         //1) 메소드명 가져오기
         System.out.println("name = " + method.getName());
         
         //2) 선언된 클래스명 가져오기
         System.out.println("class = " + method.getDeclaringClass());
         
         //3) Parameter Type 가져오기
         Class paramTypes[] = method.getParameterTypes();
         for(Class p : paramTypes) {
            System.out.println("param type : " + p);
         }
      
         //4) exception Type 가져오기
         Class exceptionTypes[] = method.getExceptionTypes();
         for(Class e : exceptionTypes) {
            System.out.println("exception type : " + e);
         }
         
         //5) return Type 가져오기
         System.out.println("return type : "+method.getReturnType());
         
         System.out.println();
      }
   }
}
 
cs

[결과]

name = privateMethod
class = class Java.reflection.ExampleClass
param type : class java.lang.Object
param type : int
exception type : class java.lang.NullPointerException
return type : int

name = addStrInt
class = class Java.reflection.ExampleClass
param type : class java.lang.String
param type : int
return type : class java.lang.String

 

3. 생성자(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
31
32
33
34
35
36
37
38
39
40
41
package Java.reflection;
 
import java.lang.reflect.Constructor;
 
//Constructor 정보 가져오기
public class Reflection3_FindConstructor {
     
   public static void main(String[] args) throws Exception {
      
      //1. class 가져오기
      Class clazz = ExampleClass.class;
      
      //2. class 에서 Constructor 가져오기
      Constructor constructors[] = clazz.getDeclaredConstructors();
      
      for(Constructor c : constructors) {
         
         //1) 생성자명 가져오기
         System.out.println("name : " + c.getName());
         
         //2) 선언된 클래스명 가져오기
         System.out.println("class : " + c.getDeclaringClass());
         
         //3) parameter type 가져오기
         Class paramTypes[] = c.getParameterTypes();
         for(Class p : paramTypes) {
            System.out.println("param type : " + p);
         }
      
         //4) exception Type 가져오기
         Class exceptionTypes[] = c.getExceptionTypes();
         for(Class e : exceptionTypes) {
            System.out.println("exception type : " + e);
         }
         
         //5) 생성자에 return type은 존재하지 않으므로 getReturnType() 메소드는 없음 
         System.out.println();
      }
   }
}
 
cs

[결과]

name : Java.reflection.ExampleClass
class : class Java.reflection.ExampleClass

name : Java.reflection.ExampleClass
class : class Java.reflection.ExampleClass
param type : class java.lang.Object
exception type : class java.lang.NullPointerException

 

4. 필드 정보 찾기

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
package Java.reflection;
 
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
 
//Field 정보 가져오기
public class Reflection4_FindFields {
   
   public static void main(String[] args) throws Exception {
      
      //1. class 가져오기
      Class clazz = ExampleClass.class;
      
      //2. class 에서 Field 가져오기
      //Field fields[] = clazz.getFields();        //public modifier(접근제한자) field 만 가져올 수 있다
      Field fields[] = clazz.getDeclaredFields();  //private modifier field 도 가져올 수 있다
      
      for(Field f : fields) {
         
         //1) 필드명 가져오기
         System.out.println("name : " + f.getName());
         
         //2) 필드가 선언된 클래스명 가져오기
         System.out.println("class : " + f.getDeclaringClass());
         
         //3) 필드 타입 가져오기
         System.out.println("type : " + f.getType());
         
         //4) 필드의 접근제한자 가져오기 (int 형이며 Modifier.toString(mod) 로 string으로 바꿔줄 수 있음)
         int modifiers = f.getModifiers();
         System.out.println("modifiers int : " + modifiers);
         System.out.println("modifiers toString : " + Modifier.toString(modifiers));
         System.out.println("isPublic? : " + (modifiers == Modifier.PUBLIC));
         
         System.out.println();
      }
   }
}
 
cs

[결과]

name : CONSTANT_FIELD
class : class Java.reflection.ExampleClass
type : class java.lang.String
modifiers int : 26
modifiers toString : private static final
isPublic? : false

name : i
class : class Java.reflection.ExampleClass
type : int
modifiers int : 1
modifiers toString : public
isPublic? : true

생략..

 

5. 메소드 실행시키기 (invoke)

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
package Java.reflection;
 
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
 
//https://kaspyx.tistory.com/80
 
//5. 메소드 실행시키기 invoke
public class Reflection5_invoke_1 {
   
   public Reflection5_invoke_1() {}
   
   public Reflection5_invoke_1(String a) {}
   
   public Reflection5_invoke_1(String a, Object b) {}
   
   public String addStrInt(String a, int b) {
      return a+b;
   }
   
   @SuppressWarnings({ "unchecked""rawtypes" })
   public static void main(String[] args) throws Exception {
      
      //1. class 가져오기
      Class clazz = Reflection5_invoke_1.class;
      
      Class paramTypes[] = new Class[2];
      paramTypes[0= String.class;
      paramTypes[1= Integer.TYPE;
      Method method = clazz.getMethod("addStrInt", paramTypes);   //* private modifier method 는 찾지 못한다
 
      Reflection5_invoke_1 methodObj = new Reflection5_invoke_1();
      Object argumentList[] = new Object[2]; 
      argumentList[0= "Str ";
      argumentList[1= new Integer(10);
      
      //java 문법은 보통 아래와 같이 S V O 
      //String rs = addStrInt("a", 10);
      //하지만, reflection 은 V.invoke(S, O)
      Object rs = method.invoke(methodObj, argumentList);
      
      System.out.println(rs);
      
   }
   
}
 
cs

[결과]

Str 10

 

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
package Java.reflection;
 
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
 
//https://kaspyx.tistory.com/80
 
//5. 메소드 실행시키기 invoke
public class Reflection5_invoke_2 {
   
   public Reflection5_invoke_2() {}
   
   public Reflection5_invoke_2(String a) {}
   
   public Reflection5_invoke_2(String a, Object b) {}
   
   public String addStrInt(String a, int b) {
      return a+b;
   }
   
   @SuppressWarnings({ "unchecked""rawtypes" })
   public static void main(String[] args) throws Exception {
      
      Method m = ExampleChildClass.class.getMethod("addStrInt"new Class[] {String.class, Integer.TYPE});
      String result = (String) m.invoke(new ExampleClass(), new Object[]{"Str "new Integer(10)});
      System.out.println(result);
      
      //위와 같다
      Method m3 = ExampleChildClass.class.getMethod("addStrInt"new Class[] {String.class, Integer.TYPE});
      String result3 = (String) m3.invoke(new ExampleClass(), new Object[]{"Str "new Integer(10)});
      System.out.println(result3);
      
      try {
         Method m2 = ExampleClass.class.getMethod("addStrInt"new Class[] {String.class, Integer.TYPE});
         String result2 = (String) m2.invoke(new ExampleSuperClass(), new Object[]{"Str "new Integer(10)});
         System.out.println(result2);
      } catch (IllegalArgumentException ie) {
         System.out.println(ie.getMessage());
      }
   }
}
 
cs

[결과]

Str 10
Str 10
object is not an instance of declaring class

 

6. 인스턴스 생성하기 (newInstance)

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
package Java.reflection;
 
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
 
//https://kaspyx.tistory.com/80
 
//5. 메소드 실행시키기 invoke
public class Reflection6_newInstance {
   
   public Reflection6_newInstance() {}
   
   public Reflection6_newInstance(String a) {}
   
   public Reflection6_newInstance(String a, Object b) {}
   
   public String addStrInt(String a, int b) {
      return a+b;
   }
   
   @SuppressWarnings({ "unchecked""rawtypes" })
   public static void main(String[] args) throws Exception {
      
      Class clazz = Reflection6_newInstance.class;
      
      Class arguTypes[] = new Class[2];
      arguTypes[0= String.class;
      arguTypes[1= Integer.TYPE;
 
      Object params[] = new Object[2];
      params[0= "Str ";
      params[1= 10;
      
      Constructor myConstructor1 = clazz.getConstructor();     
      Object myObj = myConstructor1.newInstance();             
      Method m1 = clazz.getMethod("addStrInt", arguTypes); 
      String result1 = (String) m1.invoke(myObj, params);   
      System.out.println(result1);
      
      Constructor myConstructor2 = clazz.getConstructor(String.class);
      Object myObj2 = myConstructor2.newInstance("");
      Method m2 = clazz.getMethod("addStrInt", arguTypes);
      String result2 = (String) m2.invoke(myObj2, params);
      System.out.println(result2);
      
      Constructor myConstructor3 = clazz.getConstructor(String.class, Object.class);
      Object myObj3 = myConstructor3.newInstance(""new Object());
      Method m3 = clazz.getMethod("addStrInt", arguTypes);
      String result3 = (String) m3.invoke(myObj3, params);
      System.out.println(result3);
      
      Constructor myConstructor4 = clazz.getConstructor(new Class[]{String.class, Object.class});
      Object myObj4 = myConstructor4.newInstance(""new Object());
      Method m4 = clazz.getMethod("addStrInt"new Class[] {String.class, Integer.TYPE});
      String result4 = (String) m4.invoke(myObj4, new Object[] {"String " , 11});
      System.out.println(result4);
      
   }
}
 
cs

[결과]

Str 10
Str 10
Str 10
String 11

 

위의 모든 샘플 예제들은 github(https://github.com/develo-pyo)에 올려놓았습니다.

 

참고:

https://www.geeksforgeeks.org/reflection-in-java/

https://gyrfalcon.tistory.com/entry/Java-Reflection

 

반응형

람다의 메소드 참조

메소드 참조는 매개변수의 정보 및 리턴 타입을 알아내어 람다식에 불필요한 매개 변수를 제거하기 위함

 

[기본문법]

https://developyo.tistory.com/190

 

[메소드참조]

1. static 메소드참조

public static int abs(int a){}   //Math class 내의 abs()
Math::abs  //위와 같다

Math.abs(..)와 같은 static 메소드를 Math::abs 로 표현 할 수 있다.

※ Math::abs()와 같이 ()를 붙이지 않는것을 주의.

 

위와 같은 람다의 static 메소드 참조를 사용하여 인터페이스를 구현할 경우

아래와 같이 static 메소드를 리턴하도록 구현 할 수 있다.

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
package Java.Lamda.methodReferences;
 
public class MethodRef_1_Basic {
 
    public static void main(String[] args){
        
        /** 1.기본 사용법 */
        //String.valueOf() 를 그대로 사용하여 리턴하는 람다
        FuncI funcI = (Object obj) -> String.valueOf(obj);
        System.out.println(funcI.anony("HELLO"));
        
        //위 경우 아래와 같이 메소드 참조가 가능
        FuncI funcI_ref_method = String::valueOf;
        System.out.println(funcI_ref_method.anony(100));
        
        //절대값을 구해주는 Math.abs 를 참조 
        System.out.println("abs ref : "+convert(-1, Math::abs));
    
    }
 
    @FunctionalInterface
    interface FuncI {
        String anony(Object obj);
    }
    
    @FunctionalInterface
    interface MathAbs {
        int anony(int obj);
    }
    
    public static int convert(int number, MathAbs func) {
        return func.anony(number);
    }
}
cs

[실행결과]

HELLO
100
abs ref : 1

 

2. 생성자 참조

() -> new String()    
String::new           //위와 같다

위와 같이 생성자를 리턴하는 경우 타입::new 와 같이 표현 할 수 있다.

아래는 생성자 참조를 이용한 예제.

[Person.java]

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
package Java.Lamda.methodReferences;
 
public class Person {
    
    private String name;
    private String gender;
 
    public Person() {
    }
    public Person(String name) {
        this.name=name;
    }
    public Person(String name, String gender) {
        this.name=name;
        this.gender = gender;
    }
 
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
 
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
}
 
cs

[클라이언트]

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
package Java.Lamda.methodReferences;
 
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
 
public class MethodRef_2_Constructor {
 
    public static void main(String[] args){
 
        Func<String, Person> func1 = Person::new;
        Person person = func1.create("pyo");
        System.out.println(person.getName());
        
        Func2<StringString, Person> func2 = Person::new;
        Person person2 = func2.create("pyo""male");
        System.out.println(person2.getName() + "/" + person2.getGender());
 
    }
    
    @FunctionalInterface
    interface Func<T, R>{
        R create(T t);
    }
    @FunctionalInterface
    interface Func2<T, U, R>{
        R create(T t, U u);
    }
}
 
cs

위의 Func<T, R>, Func2<T, U, R>과 비슷한 역할을 하는

Function<T, R>, BiFunction<T, U, R> functionalInterface가 java.util 패키지로 제공된다.

[실행결과]

pyo
pyo/male

 

3. 인스턴스 참조

()->a.toString();
a::toString        //위와 같다

위와 같이 인스턴스의 메소드를 리턴하는 경우 인스턴스::메소드 와 같이 표현 할 수 있다.

아래는 인스턴스 참조를 사용한 예.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package Java.Lamda.methodReferences;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
 
public class MethodRef_4_Instance {
 
    public static void main(String[] args){
        Person person = new Person("pyo");
        Func func = person::getName;
        System.out.println(func.anony());
        
    }
    
    @FunctionalInterface
    interface Func {
        String anony();
    }
}
 
cs

 

참고:

https://futurecreator.github.io/2018/08/02/java-lambda-method-references/

https://imcts.github.io/java-method-reference/

 

반응형

'back > java' 카테고리의 다른 글

HttpUrlConnection 모듈 (리펙토링) : 빌더패턴과 프록시패턴 사용  (0) 2020.02.21
[Java] java reflection  (0) 2020.02.20
[Java] lambda 람다 1 (기본문법)  (0) 2020.01.15
[Java] Generic 제네릭  (0) 2019.12.28
[Java] Compile  (0) 2019.12.22

jdk 1.8 이상부터 지원.

함수형 프로그래밍을 가능케 함.

1회용 익명 클래스가 필요할 때 람다를 사용하여 코드를 줄일 수 있음.

 

[기본 문법 1 : 인수가 없는 경우]

접근제한자, 리턴타입 생략 가능

public void method(){
}

() -> {
}

* @FunctionalInterface : 추상메소드가 한개만 존재하는 인터페이스

* lambda는 Functional Interface 에만 사용이 가능

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
package Java.Lamda.basic;
 
public class Main1 {
        
    public static void main(String[] args) {
 
        //기존방법대로 익명클래스를 사용한 구현
        Interface i = new Interface() {
            @Override
            public void sayHello() {
                System.out.println("HELLO! in anonymous class");
            }
        };
        i.sayHello();
        
        //lambda를 사용한 익명클래스 구현        
        //lambda 는 Functional Interface 에만 사용이 가능
        Interface lamda = () -> {
            System.out.println("HELLO! in anonymous method(lambda)");
        };
        lamda.sayHello();
    }
    
    //@FunctionalInterface 는 abstract method 가 오직 1개여야 한다.
    @FunctionalInterface
    interface Interface {
        void sayHello();
    }
}
 
cs

[결과]

HELLO! in anonymous class 
HELLO! in anonymous method(lambda)

 

[기본 문법 2 : 인수가 존재하는 경우]

인수 타입 생략이 가능.

return 생략 가능(함수 내 처리가 return 문이 전부인 경우)

public void method(int x, int y){
   return x+y;
}

(x, y) -> x+y;
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
package Java.Lamda.basic;
 
public class Main3 {
    
    public static void main(String[] args) {
        
        Calculation add = (x, y) -> {
            return x+y;
        };
        System.out.println(calculate(add23));
        
        
        Calculation multiply = (x, y) -> x*y;
        System.out.println(calculate(multiply, 23));
    }
    
    static Integer calculate(Calculation operation, Integer x, Integer y) {
        return operation.apply(x, y);
    }
    
    @FunctionalInterface
    interface Calculation {
        Integer apply(Integer x, Integer y);
    }
    
    class Calculator {
        Integer add(Calculation cal, Integer x, Integer y) {
            return cal.apply(x, y);
        }
    }
}

 

 

cs

[결과]

5
6

 

[기본 문법 3 : static 메소드, default 메소드]

jdk 1.8 부터 interface 에서 static 메소드, default 메소드 사용이 가능.

static 메소드 : 오버라이딩 불가
default 메소드 : 오버라이딩 가능

인터페이스에 메소드라니?!

추상메소드만 가질 수 있는게 아니라니?!

인터페이스가 메소드(default 메소드)를 가질 수 있게 되면서, 추상클래스와 인터페이스가 매우 비슷해졌다.

(인터페이스를 만들 때, 공통 관심사는 default 메소드로, 나머지 메소드를 abstract 메소드로 선언하면 기존의 추상클래스와 매우 비슷하다)

자바에서도 결국 다중상속이 가능하게 된 셈이다.

그렇다면 추상클래스와 인터페이스의 차이점은 이제 더이상 없는건가?

아직 아래와 같은 차이점이 존재한다.

추상클래스 : 다중상속이 불가, 인스턴스 변수를 가질 수 있다
인터페이스 : 다중구현이 가능, 인스턴스 변수를 가질 수 없다

Calculation : interface

ExtendsCalculation : Calculation interface 상속받은 interface

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
package Java.Lamda.basic;
 
public class Main4 {
 
    public static void main(String[] args) {
 
        Calculation add = (x, y) -> {
            return x+y;
        };
        add.print(23);
        Calculation.print2(23);
        System.out.println(calculate(add23));
        
        
        ExtendsCalculation multiply = (x, y) -> x*y; 
        multiply.print(23);
        System.out.println(calculate(multiply, 23));
        
    }
    
    static Integer calculate(Calculation operation, Integer x, Integer y) {
        return operation.apply(x, y);
    }
    
    @FunctionalInterface
    interface Calculation {
        Integer apply(Integer x, Integer y);
        
        default void print(Integer x, Integer y) {
            System.out.println("x : " + x);
            System.out.println("y : " + y);
        }
        
        static void print2(Integer x, Integer y) {
            System.out.println("x : " + x + ", y : " + y);
        }
    }
    
    class Calculator {
        Integer add(Calculation cal, Integer x, Integer y) {
            return cal.apply(x, y);
        }
    }
    
    @FunctionalInterface
    interface ExtendsCalculation extends Calculation{
        @Override
        default void print(Integer x, Integer y){
            System.out.println("x,y : " + x +","+ y);
        }
    }
    
}
 
cs

[결과]

x : 2
y : 3
x : 2, y : 3
5
x,y : 2,3
6

※ stackoverflow 및 각종 개발자 커뮤니티 QnA 에 Comparator Interface 구현시 람다를 어떻게 사용할 수 있냐는 질문이 꽤 많이 보인다.

1.8 에서의 Comparator 인터페이스를 들여다 보면, default 메소드 및 static 메소드 다수 존재하나 추상메소드는 오직 compare()메소드, 단 한 개 존재한다.

 

[메소드 참조]

https://developyo.tistory.com/193?category=688588

 

 

참고:

https://futurecreator.github.io/2018/07/20/java-lambda-type-inference-functional-interface/

 

반응형

'back > java' 카테고리의 다른 글

[Java] java reflection  (0) 2020.02.20
[Java] lambda 람다 2 (메소드참조)  (0) 2020.01.17
[Java] Generic 제네릭  (0) 2019.12.28
[Java] Compile  (0) 2019.12.22
[Java] Collection Framework  (0) 2019.12.21

+ Recent posts