20 Oct 2019
|
Java
Java 8 in Action(Chapter 2)
동작 파라미터화 코드 전달하기.
1. 동작 파라미터화에서는 메서드 내부적으로 다양한 동작을 수행할 수 있도록 코드를 메서드 인수로 전달한다.
public static void main(String[] args){
// ApplePredicate 인터페이스(동작) 타입을 파라미터로 받아 사과를 필터링한다.
public static List<Apple> filterByApplePredicate(List<Apple> apples, ApplePredicate p) {
List<Apple> result = new ArrayList<>();
for(Apple apple : apples){
if(p.test(apple)){ // 필터링하는 동작을 수행
result.add(apple);
}
}
return result;
}
}
public interface ApplePredicate {
boolean test(FilteringApplesClone.Apple apple);
}
// 무게를 필터링하는 클래스
public class AppleHeavyWeightPredicate implements ApplePredicate {
@Override
public boolean test(FilteringApplesClone.Apple apple) {
return apple.getWeight() > 150;
}
}
// 색깔을 필터링하는 클래스
public class AppleGreenColorPredicate implements ApplePredicate {
@Override
public boolean test(FilteringApplesClone.Apple apple) {
return "green".equals(apple.getColor());
}
}
2. 동작 파라미터화를 이용하면 변화하는 요구사항에 더 잘 대응할 수 있는 코드를 구현할 수 있으며 나중에 엔지니어링 비용을 줄일 수 있다.
- 중복된 코드를 줄이며, 코드 관리가 용이하다.
3. 코드 전달 기법을 이용하면 동작을 메서드의 인수로 전달할 수 있다. 하지만 자바8 이전에는 코드를 지저분하게 구현해야 했다.
익명 클래스로도 어느 정도 코드를 깔끔하게 만들 수 있지만 자바 8에서는 인터페이스를 상속받아 여러 클래스를 구현해야 하는 수고를 없앨 수 있는 방법을 제공한다.
public static void main(String[] args){
// 자바8 이전 코드
filterByApplePredicate(apples, new ApplePredicate(){
@Override
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
});
//
// 자바 8 람다를 이용한 코드.
filterByApplePredicate(apples, (Apple a) -> "green".equals(a.getColor()));
}
- 자바 API의 많은 메서드는 정렬, 스레드, GUI 처리 등을 포함한 다양한 동작으로 파라미터화 할 수 있다.
public static void main(String[] args){
new Thread(() -> System.out.println("Hello world"));
}
ref. http://www.hanbit.co.kr/store/books/look.php?p_code=B1999551123
19 Oct 2019
|
DesignPattern
Stratagy(전략) 패턴
- 전략 패턴은 각 알고리즘을 캡슐화하는 알고리즘 패밀리를 정의해둔 다음에 런타임에 알고리즘(전략)을 선택하는 기법이다.
전략이란
- 어떤 목적을 달성하기 위해 일을 수행하는 방식, 비즈니스 규칙, 문제를 해결하는 알고리즘
사과를 필터링 하는 행위를 전략패턴으로 구현해보자.
요구사항
1) color = “green”인 사과를 구하라.
2) weight > 150인 사과를 구하라.
다음과 같은 요구사항이 있을 경우 가장 직관적인 방법은
요구사항별로 각각 구현하는 것이다.
public static void main(String[] args) {
List<Apple> apples = Arrays.asList(new Apple("green",80)
, new Apple("red",155)
, new Apple("yellow",122));
// 요구사항 1
List<Apple> greenApples = filterGreenApples(apples);
// 요구사항 2
List<Apple> heavyApples = filterHeavyApples(apples);
}
public static class Apple{
private String color = "";
private int weight = 0;
public Apple(String color, int weight) {
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Apple{" +
"color='" + color + '\'' +
", weight=" + weight +
'}';
}
}
public static List<Apple> filterGreenApples(List<Apple> apples){
List<Apple> result = new ArrayList();
for(Apple apple : apples){
if("green".equals(apple.getColor())){
result.add(apple);
}
}
return result;
}
public static List<Apple> filterHeavyApples(List<Apple> apples){
List<Apple> result = new ArrayList();
for(Apple apple : apples){
if(apple.getWeight() > 150){
result.add(apple);
}
}
return result;
}
그러나 요구사항이 계속 추가 될때마다 위 방식처럼 필터 메서드를 추가하는 방법이 좋은 방법일까?
당장 filterGreenApples() 메서드와 filterHeavyApples() 메서드만 보더라도
if문의 조건 한 줄을 제외하면 모두 중복된 코드로 이루어져 있다.
조건들을 클래스로 캡슐화하고 조건이 바뀔때마다 클래스를 동적으로 바꿔칠 수 있다면
중복없이 코드를 작성 할 수 있을 것이다.
구현 방법은 아래와 같다.
// 인수로 값을 받아 true나 false를 반환하는 함수를 predicate라 한다.
public interface ApplePredicate {
boolean test(FilteringApplesClone.Apple apple);
}
public class AppleGreenColorPredicate implements ApplePredicate {
@Override
public boolean test(FilteringApplesClone.Apple apple) {
return "green".equals(apple.getColor());
}
}
public class AppleHeavyWeightPredicate implements ApplePredicate {
@Override
public boolean test(FilteringApplesClone.Apple apple) {
return apple.getWeight() > 150;
}
}
public static List<Apple> filterByApplePredicate(List<Apple> apples, ApplePredicate p) {
List<Apple> result = new ArrayList<>();
for(Apple apple : apples){
if(p.test(apple)){ // Predicate
result.add(apple);
}
}
return result;
}
이를 다이어그램으로 표현하면 다음과 같다.

ApplePredicate 인터페이스는 구현체(AppleGreenColorPredicate, AppleHeavyWeightPredicate)를 캡슐화한다.
즉,런타임 시점까지 어떠한 알고리즘을 선택할지 은닉화한다.
AppleHeavyWeightPredicate, AppleGreenColorPredicate 클래스를 전략이라 하고
ApplePredicate 인터페이스처럼 전략을 캡슐화하는 객체를 알고리즘 패밀리라 한다.
public static void main(String[] args){
// 런타임에 어떤 전략을 선택할 지 선택할 수 있다.
List<Apple> filteredByGreenColor = filterByApplePredicate(apples, new AppleGreenColorPredicate());
List<Apple> filteredByfilterByWeight = filterByApplePredicate(apples, new AppleHeavyWeightPredicate());
}
ref. https://gmlwjd9405.github.io/2018/07/06/strategy-pattern.html
ref. http://www.hanbit.co.kr/store/books/look.php?p_code=B1999551123
18 Oct 2019
|
Java
Java 8 in Action(Chapter 1)
자바 8의 핵심 개념
1. 동작 파라미터화 코드
- 메서드와 람다를 일급 시민으로 사용하여 요구사항에 유연하게 대처할 수 있다.
-
일급 시민: 파라미터로 전달할 수 있는 것, ex. 원시 타입, 객체
비슷한 동작을 하는 메서드끼리 중복되는 코드가 발생한다.
filterGreenApples()와 filterHeavyApples()를 보자.
무게와 색깔을 확인하는 동작(if문)을 제외하고는 코드가 중복된다.
public class Apple{
private String color = "";
private int weight = 0;
public Apple(String color, int weight) {
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Apple{" +
"color='" + color + '\'' +
", weight=" + weight +
'}';
}
}
public static List<Apple> filterGreenApples(List<Apple> apples){
List<Apple> result = new ArrayList();
for(Apple apple : apples){
if("green".equals(apple.getColor())){
result.add(apple);
}
}
return result;
}
public static List<Apple> filterHeavyApples(List<Apple> apples){
List<Apple> result = new ArrayList();
for(Apple apple : apples){
if(apple.getWeight() > 150){
result.add(apple);
}
}
return result;
}
Predicate 인터페이스나 lambda를 사용한다면 중복된 코드를 줄이고 메서드의 재사용성을 높일 수 있다.
- Predicate란?
- 수학에서는 인수로 값을 받아 true나 false를 반환하는 함수를 predicate라 한다.
// filterGreenApples()와 filterHeavyApples()뿐만 아니라 추가 요구사항이 발생 하더라도
// 코드 변경없이 처리 가능하다.
public static List<Apple> filterApples(List<Apple> apples, Predicate<Apple> p){
List<Apple> result = new ArrayList<>();
for(Apple apple : apples){
if(p.test(apple))
result.add(apple);
}
return result;
}
public static boolean isHeavyApple(Apple apple){
return apple.getWeight() > 150;
}
public static boolean isGreenApple(Apple apple){
return "green".equals(apple.getColor());
}
public static void main(String[] args){
// 객체의 레퍼런스를 파라미터로 넘길 수 있듯이, 클래스::메서드 문법을 통해 메서드 레퍼런스를 넘길 수 있다.
List<Apple> colorFiltered = filterApples(inventory, FilteringApples::isGreenApple);
System.out.println(colorFiltered);
List<Apple> weightFiltered = filterApples(inventory, FilteringApples::isHeavyApple);
System.out.println(weightFiltered);
// lambda(익명 함수)를 이용하여 처리하는 방식. 일회성으로 사용할 경우에 사용한다.
List<Apple> lambdaFiltered = filterApples(inventory, (Apple a) -> "green".equals(a.getColor()));
}
2.스트림 API (컬렉션 프레임워크와 함께 사용된다)
- 필터링
- 추출
- 그룹화
- 포킹
- filter를 두 개의 CPU로 포킹(분산처리) 한 다음 결과를 합침.
- 접근방식의 차이
- 컬렉션 : 어떻게 데이터를 저장하고 접근할지에 중점.
- 스트림 : 데이터에 어떤 계산을 할 것인지 묘사하는 것에 중점.
스트림은 스트림 내의 요소를 쉽게 병렬로 처리.
컬렉션을 필터링할 수 있는 가장 빠른 방법은 컬렉션을 스트림으로 바꾸고,
병렬로 처리한 다음 리스트로 다시 복원하는 것.
색깔별로 사과를 그룹화 해야 한다고 가정해보자.
Stream API를 사용하지 않고 구현한 코드이다.
Map<String,List<Apple>> appleByColor = new HashMap();
for(Apple apple : List<Apple> apples){
String color = apple.getColor();
List<Apple> appleForColor = appleByColor.get(color);
if(appleForColor == null){ // 색깔별로 그룹화된 리스트가 없다면 새로 만든다.
appleForColor = new ArrayList();
appleByColor.put(color,appleForColor);
}
appleForColor.add(apple);
}
위 코드를 StreamAPI를 이용하면 단 한줄로 표현할 수 있다.
Map<String,List<Apple>> appleByColor = apples.stream().collect(Collectors.groupingBy(Apple::getColor));
3. 인터페이스에 디폴트 메서드 추가
1) 확장성을 위해 추가하였다.
- Stream(), parallelStream()의 경우 Collection 인터페이스에 추가되었는데 이 메서드들이
default 메서드이 아닌 기존의 추상메서드로 선언되었다면, 기존에 존재하는 컬렉션 프레임워크 전체가 Stream(), parallelStream() 메서드를 구현해야 하므로
매우 큰 비용이 발생한다. 이러한 문제를 피하기 위해 탄생.
ref. http://www.hanbit.co.kr/store/books/look.php?p_code=B1999551123
17 Oct 2019
|
Java
-
자바8 기준으로 작성된 내용임을 밝힙니다.
-
클래스 로더

1) Bootstrap or Primordial ClassLoader
- Bootstrap class loader는 모든 클래스 로더들의 최상위 클래스로더이며, core JVM의 일부로서 네이티브 코드로 구현되어 있다.
2) Extension ClassLoader
- Bootstrap class loader의 자식 클래스 로더이며, core java class의 extensions를 로딩하는데 사용된다.
3) Application or System ClassLoader
- Extension class loader의 자식 클래스 로더이며, 어플리케이션 레벨 클래스를 로딩( classpath에 존재하는 클래스) 하는 데 사용한다.

1) Deligation
- 클래스 로딩과정은 Deligation Model을 따른다.
- Deligation Model은 클래스 로딩을 상위 클래스 로더에게 위임하는 것이다.
Application ClassLoader -> Extension ClassLoader -> Bootstrap ClassLoader로 위임되며
Bootstrap ClassLoader -> Extension ClassLoader -> Application ClassLoader 순으로 내려오며 클래스를 로딩한다.
2) Uniqueness
- 하위 클래스로더는 상위 클래스로더가 로딩한 클래스를 다시 로딩하지 않게 해서 로딩된 클래스의 유일성을 보장한다. <br>
3) Visibility
- 하위 클래스로더는 상위 클래스로더가 로딩한 클래스를 볼 수 있지만, 상위 클래스로더는 하위 클래스로더가 로딩한 클래스를 볼 수 없다.
- 예를 들어 Bootstrap ClassLoader가 로딩한 rt.jar의 기본 클래스(Object,String,Long,Integer…)를 개발자가 만든 클래스를 로딩하는 Application ClassLoader에서 볼 수 없다면 애플리케이션이 제대로 동작하지 않을 것이다.
ref. https://docs.oracle.com/javase/8/docs/technotes/tools/windows/findingclasses.html#A101244
ref. https://www.baeldung.com/java-classloaders
ref. https://homoefficio.github.io/2018/10/13/Java-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A1%9C%EB%8D%94-%ED%9B%91%EC%96%B4%EB%B3%B4%EA%B8%B0/
ref. https://dev.vividbreeze.com/jvm-classloading/
16 Oct 2019
|
Java
자바언어로 프로그래밍하면서 공식처럼 사용하던 main() 메서드가 왜
public static void main(String args[])인지 궁금해져 찾아보았다.
// main() 메서드
public static void main(String args[]){
}
자바 어플리케이션을 실행하면 자바 인터프리터에 의해 main() 메서드가 가장 먼저 호출된다.
메서드를 하나씩 뜯어보면
1) 접근 제어자는 public
- 클래스 외부에서 main() 메서드를 호출하므로 접근제어자는 public이여야 한다.
2) static keyword
3) void
4) main(String args[])
- 메서드명은 main이며, String 배열을 argument로 가진다.
intellJ에서는 static 키워드를 제외하면 실행이 되지 않음을 미리 알려준다.
2.png)
ref. http://cs-fundamentals.com/tech-interview/java/why-does-main-method-have-to-be-static.php