반응형
반응형

 

 

 

반응형
반응형

Enum은 열거형이라는 뜻을 가지고 있다 Enum이라는 타입을 사용할 시 Enum에서 설정한 데이터만 사용할 수 있다 예를 들면 '요일' "월, 화, 수, 목, 금, 토, 일"이 있다 해당 요일 외에 데이터는 받지 않기 때문에 컴파일 에러로 사전에 버그를 잡아낼 수 있다

 

Enum에 다양한 메소드가 존재한다

 

📝name()

enum Week {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}



Week week = Week.FRIDAY;

String weekName = week.name();
System.out.println(weekName); // FRIDAY

enum의 값을 String으로 반환해준다

 

📝예제

import java.util.Arrays;

public enum AreaEnum {

    SEOUL("서울",1),
    INCHEON("인천",2),
    GYEONGGI("경기",31);

    private final String name;
    private final int code;


    AreaEnum(String name, int code) {
        this.name = name;
        this.code = code;
    }

    public String getName() {
        return this.name;
    }

    public int getCode() {
        return this.code;
    }


    public static String getByCode(int code) {
        Arrays.stream(values())
                .filter(area -> area.getCode() == code)
                .findFirst()
                .map(AreaEnum::getName)
                .orElse(null);
    }

}
반응형
반응형
public class SwitchExample {
    public static void main(String[] args) {
        int dayOfWeek = 3;

        String dayType = switch (dayOfWeek) {
            case 1, 2, 3, 4, 5 -> "Weekday";
            case 6, 7 -> "Weekend";
            default -> throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeek);
        };

        System.out.println("Day type: " + dayType);
    }
}

Java17부터 →을 이용해 각각 break를 안 사용해도 되고 가독성을 높여주는 Syntactic Sugar이다

반응형
반응형

📝메소드 레퍼런스

메소드 레퍼런스란 클래스에 포함된 메소드를 생략해 표현이 가능하다 이 점을 이용해 간략할 수도 있고 무엇보다 일급객체의 특징인 파라미터에 함수를 넣을 수 있다. (물론 적절한 함수형 인터페이스의 시그니처[함수형 인터페이스에 맞는 포맷]이면 메소드 레퍼런스를 안 쓰고도 가능하다)

 

  • System.out::println
Arrays.asList(1, 2, 3, 4, 5)
      .forEach(System.out::println);

forEach안에 System.out::println [클래스명:메소드명]을 넣을 수 있다.

함수형 인터페이스를 이용하면 파라미터 및 반환타입 추론이 가능하기 때문에 생략할 수 있다.

 

  • 나만의 클래스:메소드를 축약해서 사용해보기

public class MethodLambdaTest {

    @Test
    public void testing(){

        System.out.println(
            Arrays.asList(new BigDecimal("10.0"), new BigDecimal("23"), new BigDecimal("5"))
                  .stream()
                   // .sorted()                                 // ✅ 동일
                   // .sorted((bd1, bd2) -> bd1.compareTo(bd2)) // ✅ 동일
                   // .sorted(BigDecimalUtil::compare)          // ✅ 동일
                  .sorted(BigDecimal::compareTo)                // ✅ 동일
                  .collect(toList())
        ); // [5, 10.0, 23]
    }
}

class BigDecimalUtil {
    public static int compare(BigDecimal bd1, BigDecimal bd2){
        return bd1.compareTo(bd2);
    }
}

 

  • sorted 함수 설명
Stream<T> sorted(Comparator<? super T> comparator) 
...

@FunctionalInterface
public interface Comparator<T> {
   int compare(T o1, T o2)
}

 

sorted는 Comparator를 받고 Comparator의 경우 (T, T) -> int이기 때문에 생략해도 알아서 비교값들이 들어간다

 

 

 

  • anyMatch에 equals 함수 적용시키기
@Test
public void testing(){
	final String targetString = "c";
    System.out.println(
        Arrays.asList("a", "b", "c", "d")
                .stream()
                //.anyMatch(x -> x.equals("c")) // ✅ 동일
                //.anyMatch(String::equals)     // ❌ 동작 X
                .anyMatch(targetString::equals) // ✅ 동일
                //.anyMatch("c"::equals)        // ✅ 동일
    );
}

 

  • anyMatch에 String::equals 동작 안 하는 이유
boolean anyMatch(Predicate<? super T> predicate);

...

@FunctionalInterface
public interface Comparator<T> {
   int compare(T o1, T o2)
}

anyMatch의 경우 Predicate 받고 Predicate의 경우 T -> boolean이기 때문에 String::equals의 반환 타입과 맞지만
"c".eqauls("d")와 같이 두개의 비교값 즉, 두개 파라미터가 필요하기 때문에 시그니처가 맞지 않아서 동작하지 않는다.

반응형
반응형

📝Stream

Java8에서 나온 것으로 매개변수들이 함수형 인터페이스 기반으로 만들어져 Stream에서 쓰는 내부 함수들은 Predicate, Function처럼 함수형 인터페이스를 사용한다

// Stream 인터페이스 내부 함수
Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

Stream 장점으로는 Lazy Evaluation으로 이 의미는 즉, 불필요한 연산을 피한다는 의미이다.

배열의 값을 하나씩 읽어서 순차적으로 진행시킨다.  밑에 코드에서 보는 것 처럼 i < 6이라는 조건절이 있는 경우 1 ~ 10까지 i < 6처리한 후에 남은 애들이 2번째  조건 절인 i % 2 == 0을 거치는게 아니라 "1"이 i < 6을 거친 후 i % 2 == 0을 거치는 순차적인 Iterator로 돌아가게 된다.

 

Stream 동작 트래킹

@Test
public void test(){
    final List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

    System.out.println(
        list.stream()
            .filter(i -> {
                System.out.println(i + " : i < 6");
                return i<6;
            })
            .filter(i -> {
                System.out.println(i + " : i % 2 == 0");
                return i%2==0;
            })
            .collect(Collectors.toList())
    );
    /**
     * 1 : i < 6
     * 1 : i % 2 == 0
     * 2 : i < 6
     * 2 : i % 2 == 0
     * 3 : i < 6
     * 3 : i % 2 == 0
     * 4 : i < 6
     * 4 : i % 2 == 0
     * 5 : i < 6
     * 5 : i % 2 == 0
     * 6 : i < 6
     * 7 : i < 6
     * 8 : i < 6
     * 9 : i < 6
     * 10 : i < 6
     * [2, 4]
     */
}

 

여기서 예제를 보면 1 ~ 10을 1번 필터를 다 거친 후 남은 걸 2번 필터를 거치거나 각 Iterator들을 개별로 1번 → 2번을 거치거나 별 차이는 없어보인다 하지만 밑에 같은 예제의 경우 이야기가 달라진다. findFirst로 1개의 정상 값만 나오면 바로 종료시켜버린다 물론 IF로직같은 걸로 1개 일 때 종료하던가 로직 처리가 가능하지만 가독성 및 효율성이 떨어진다.

 

Stream 동작 트래킹

@Test
public void test(){
    final List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

    System.out.println(
        list.stream()
            .filter(i -> {
                System.out.println(i + " : i < 6");
                return i<6;
            })
            .filter(i -> {
                System.out.println(i + " : i % 2 == 0");
                return i%2==0;
            })
            .findFirst()
    );
    /**
     * 1 : i < 6
     * 1 : i % 2 == 0
     * 2 : i < 6
     * 2 : i % 2 == 0
     */
}

 

동작 방식을 이제 알아봤으니 이걸 이용해 익숙해져보자

 

Stream 사용해보기

package hello.exception;

import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

public class StreamTest {

    @Test
    public void intStreamTest(){

        /** ---- 연속적인 값 할당 ---- **/
        IntStream.range(0, 10).forEach(i -> System.out.println("range : " + i)); // range : 0 range : 1 .... range : 9
        // IntStream.iterate(1, i -> i + 1).forEach(i -> System.out.println("iterate : " + i)); // iterate : 1 ... iterate : 21
        IntStream.rangeClosed(0, 10).forEach(i -> System.out.println("rangeClose : " + i)); // rangeClose : 0 rangeClose : 1 ... rangeClose : 10

        /** ---- Sum ---- **/
        long sum = IntStream.of(1, 2, 3, 4, 5)
                .sum();

        System.out.println("sum : " + sum); // sum : 15

    }

    @Test
    public void streamTest(){

        /** ---- of, forEach ---- **/
        Stream.of(1, 2, 3, 4, 5)
                .forEach(i -> System.out.println("stream of : " + 1)); // stream of : 1 ... stream of : 5

    }

    @Test
    public void streamChaningTest(){

        /** ---- stream chaning ---- **/
        final List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Optional<Integer> result =
                numbers.stream() // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
                        .filter(number -> number > 3) // 4, 5, 6, 7, 8, 9, 10
                        .filter(number -> number < 9) // 4, 5, 6, 7, 8
                        .map(number -> number * 2)    // 8, 10, 12, 14, 16
                        .filter(number -> number < 15) // 8, 10, 12, 14
                        .findAny(); // 8

        System.out.println("stream result : " + result.get());


        // Intermediate Operation Method [Stream 리턴 -> chaning 가능]
        // Terminated Operation Method   [Stream 외 리턴]
    }
    @Test
    public void streamChaningTest2(){

        final List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        /** ---- collection(toList()) ---- **/
        List<String> result = numbers.stream()
                .filter(number -> number > 8) // 9, 10
                .map(number -> "#" + number)
                .collect(toList());

        System.out.println("result : " + result); // result : [#9, #10]

        /** ---- collection(joining) ---- **/
        String result2 = numbers.stream() // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
                .filter(number -> number > 8) // 4, 5, 6, 7, 8, 9, 10
                .map(number -> "#" + number)
                .collect(joining(",","[","]"));

        System.out.println("result2 : " + result2); // result2 : [#9,#10]

        /** ---- collection(toSet()) ---- **/
        Set<String> result3 = numbers.stream() // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
                .filter(number -> number > 8) // 4, 5, 6, 7, 8, 9, 10
                .map(number -> "#" + number)
                .collect(toSet());

        System.out.println("result3 : " + result3); // result3 : [#10, #9]

        /** ---- distinct ---- **/
        List<String> result4 =
                Stream.of(1, 3, 3, 5, 5)
                        .filter(number -> number > 2)
                        .map(number -> number * 2)
                        .map (number -> "#" + number) // [#6, #6, #10, #10]
                        .distinct()                   // [#6, #10]
                        .collect(toList());

        System.out.println("result4 : " + result4); // result4 : [#6, #10]

        /** —— count —— **/
        long result5 =
                Stream.of(1, 3, 3, 5, 5)
                        .filter(number -> number > 2)
                        .map(number -> number * 2)
                        .map (number -> "#" + number) // [#6, #6, #10, #10]
                        .distinct()                   // [#6, #10]
                        .count();

        System.out.println("result5 : " + result5); // result5 : 2

    }
    
	@Test
    public void reduceTest(){
        /** —— reduce —— **/
        List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        Integer result =
                numbers.stream()
                        .reduce(100, (x, y) -> {
                            System.out.println("x : " + x + ", y :" + y);
                            return x+y;
                        });

        // reduce : 반복된 패턴을 추상화 할 수 있다.
        System.out.println("result : " + result);

        /**
         * x : 100, y :1
         * x : 101, y :2
         * x : 103, y :3
         * x : 106, y :4
         * x : 110, y :5
         * x : 115, y :6
         * x : 121, y :7
         * x : 128, y :8
         * x : 136, y :9
         * x : 145, y :10
         * result : 155
         */

    }

}

 

 

 

🔗 참고 및 출처

https://dororongju.tistory.com/137

반응형
반응형

📝Optional

개발할 때 가장 많이 발생하는 예외중 하나는 NPE로서 NPE를 피하려면 null 여부 검사를 해야하는데 코드가 복잡하고 번거롭다 Java8에서 나온 Optional<T> 클래스를 이용해 NPE를 방지할 수 있도록 도와준다.

 

  • 참고로 자바스크립트에서는 Optional사용시 null이나 undefinedundefined를 반환시킨다 [Null에 대한 에러 발생 핸들링]

 

 

Optional 사용해보기

package hello.exception;

import java.util.Optional;

public class Main {
    public static void main(String[] args) throws Exception{

        /** ---- optional 값 체크 ---- **/
        Optional<String> optVal0 = Optional.of("abc");
        Optional<String> optVal2 = Optional.ofNullable(null); // Null 체크 NPE 발생 X
        // Optional<String> optVal1 = Optional.of(null);      // NullPointerException 발생

        System.out.println(optVal2);
        // hello 값이 있는 경우 : Optional[hello]
        // null인 경우         : Optional.empty

        /** ---- optional 초기화 ---- **/
        Optional<String> optVal3 = null;             // 사용 금지!!
        Optional<String> optVal4 = Optional.empty(); // 정상 초기화 작업

        /** ---- optional 값 체크해서 Null 핸들링 하기 ---- **/
        Optional<String> optVal = Optional.ofNullable("test");
        String str1 = optVal.get();                                  // optVal에 저장된 값 반환, Null이면 예외 발생
        String str2 = optVal.orElse("");                             // optVal에 저장된 값 반환, Null이면 orElse안에 값 반환    [많이 사용] | 매개변수 : T
        String str3 = optVal.orElseGet(()->"");                      // optVal에 저장된 값 반환, Null이면 orElseGet안에 값 반환 [많이 사용] | 매개변수 : Supplier
        String str4 = optVal.orElseThrow(NullPointerException::new); // null이면 false, 아니면 true를 반환

        // ifPresent -> null이 아닐 때만 수행한다
        Optional.ofNullable("notNull").ifPresent(System.out::println);

    }

}

 

 

orElse vs orElseGet 

package hello.exception;


import org.junit.jupiter.api.Test;

import java.util.Optional;

public class orElseTest {

    @Test
    public void testOptionalElse(){

        String notNull ="not Null";
        String nulll = null;

        String resultNullOrElse = Optional.of(notNull).orElse(welcome());           // welcome 함수 실행
        String resultNotNullOrElse = Optional.ofNullable(nulll).orElse(welcome());  // welcome 함수 실행

        System.out.println("notNullOrElse : " + resultNullOrElse); // notNullOrElse : not Null
        System.out.println("NullOrElse : " + resultNotNullOrElse); // NullOrElse : WelComeTo Blog

        System.out.println("===============================");

        String resultNullOrElseGet = Optional.of(notNull).orElseGet(this::welcome);           // doesn't call welcome
        String resultNotNullOrElseGet = Optional.ofNullable(nulll).orElseGet(this::welcome);  // welcome 함수 실행

        System.out.println("notNullOrElseGet : " + resultNullOrElseGet); // notNullOrElseGet : not Null
        System.out.println("NullOrElseGet : " + resultNotNullOrElseGet); // NullOrElseGet : WelComeTo Blog

        /**
         * ---- 출력결과
         * welcome!!
         * welcome!!
         * notNullOrElse : not Null
         * NullOrElse : WelComeTo Blog
         * ===============================
         * welcome!!
         * notNullOrElseGet : not Null
         * NullOrElseGet : WelComeTo Blog
         *
         */

    }

    public String welcome(){
        System.out.println("welcome!!");
        return "WelComeTo Blog";
    }
}

 

 

orElse의 경우 (T other)로 값을 받는데 얘는 Eager Evaluation으로 동작하기 때문에

orElseGet의 경우 Supplier의 get을 이용하는데 Supplier의 get이 Lazy Evaluation으로 동작하기 때문에 결과가 정확해야지만 동작하게 됩니다. 그래서 Null인 경우 아예 동작을 하지 않는 것이죠

 

함수형 인터페이스를 사용하는 경우 거의 Lazy Evaluation하고 관련이 있기 때문에 비슷한 녀석들이 있으면 꼭 잘 보고 사용하시길 바랍니다.

 

 

🔗 참고 및 출처

https://erjuer.tistory.com/102

반응형
반응형

 

📝BigDecimal

Java 언어에서 숫자를 정밀하게 저장하고 표현할 수 있는 유일한 방법이다.
소수점을 저장할 수 있는 가장 크기가 큰 타입인 double은 소수점의 정밀도에 있어 한계가 있어 값이 유실될 수 있다.
Java 언어에서 돈과 소수점을 다룬다면 BigDecimal은 선택이 아니라 필수이다

 

BigDecimal 단점

  • 느린 연산 처리 속도
  • 기보타입에 비해 많은 양의 리소스 차지
  • 기본 타입보다 조금 불편한 사용법

 

double vs BigDecimal

  • double 타입
    • 내부적으로 수를 저장할 때 이진수의 근사치를 저장한다 (64비트 부동 소수점 자료형)
  • BigDecimal 타입
    • 내부적으로 수를 십진수로 저장하여 아주 작은 수과 큰 수의 연산에 대해 거의 무한한 정밀도를 보장한다

 

package hello.exception;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;

public class study {

    public static void main(String[] args) {

        BigDecimal bigDecimalA = new BigDecimal("10.0000000000");
        BigDecimal bigDecimalB = new BigDecimal("3.000000000");

        DecimalFormat decimalFormat = new DecimalFormat("0.0000000000000000000000");

        double a = 10.0000000000;
        double b = 3.000000000;

        /** a / b -> 기대값: 3.333333333333 **/

        System.out.println(decimalFormat.format(a / b)); // 출력값 : 3.3333333333333335000000


        // 20자리까지 표현하고 그 이후에는 반올림을 한다
        BigDecimal result = bigDecimalA.divide(bigDecimalB, 20, RoundingMode.HALF_UP);
        System.out.println(result); // 출력값 : 3.33333333333333333333


    }
}

 

🔗 참고 및 출처

https://jsonobject.tistory.com/466#:~:text=BigDecimal%EC%9D%80%20Java%20%EC%96%B8%EC%96%B4%EC%97%90%EC%84%9C,%EC%9D%B4%20%EC%9C%A0%EC%8B%A4%EB%90%A0%20%EC%88%98%20%EC%9E%88%EB%8B%A4.

반응형
반응형

📝익명 클래스

프로그램에서 일시적으로 한번만 사용되고 버려지는 객체입니다.

일시적으로 사용된다는 것은 재사용이 되지 않거나 그럴 필요가 없다는 것인데 이건 확장성이 좋지 않다는 걸 의미합니다.

OOP 측면에서는 확장성 있게 만드는 걸 지향하지만 오히려 설계를 해치는 경우가 있습니다.

 

이러한 경우에 익명 클래스를 많이 씁니다.

 

    1. 프로그램 한번만 사용되어야하는 객체의 경우 [ UI 이벤트 처리, 스레드 객체 등... ]

 

밑에는 UI 이벤트 처리에 대한 예제이다 사실상 어거지로 만든 거고 클래스로도 처리가 가능할 거 같은데 설계 부분에 솔직히 이해는 가지만 크게 와닿진 않는다

 

Button를 생성해서 인터페이스를 즉각 구현해서 익명 객체(매개변수)를 구현한 예제이다.

 

RingListener

package org.example;

public interface RingListener {

    public void ring ();
}

 

Button

package org.example;

public class Button {
    public void ring(RingListener listener){
        listener.ring();
    }
}

 

Main

package org.example;

public class Main {

    public static void main(String[] args) {

    Button button = new Button();
    button.ring(new RingListener() {
        @Override
        public void ring() {
            System.out.println("1회용 ring ring");
        }
    }); // 1회용 ring ring
  }
}

 


    2. 비즈니스 로직이 제각각이며 재사용성이 없어서 매번 클래스를 생성해야하는 경우 [ 아래와 예제와 같이 버튼이 여러개인데 각각 다른 역할의 응답을 하는 경우 버튼이 많아질수록 기하급수적으로 늘어난다]

 

Button

package org.example;

public class Button {

    void turnOn () {
        System.out.println("버튼을 킵니다.");
    }
}

 

Anonymous

package org.example;

public class Anonymous {


    /** 객체 생성해 재정의하기 **/
    Button button = new Button(){
        String name = "방 스위치";

        // 익명 클래스 안에서만 살아있기 때문에 사용 불가
        void turnOff(){
            System.out.println("Down 버튼을 눌러 " + name + "를 끕니다");
        }

        // 재정의를 했기 때문에 사용 가능
        @Override
        void turnOn(){
            System.out.println("Up 버튼을 눌러 " + name + "를 킵니다");
        }
    };


    /** 클래스안 메소드에서 객체 생성해 재정의하기 [1번과 동일하다] **/
    void method1(){

        Button button1 = new Button(){

            String name = "정답 스위치";

            // 익명 클래스 안에서만 살아있기 때문에 사용 불가
            void turnOff(){
                System.out.println("X 버튼을 눌러 " + name + "를 끕니다");
            }

            // 재정의를 했기 때문에 사용 가능
            @Override
            void turnOn(){
                System.out.println("O 버튼을 눌러 " + name + "를 킵니다");
            }

        };

        button1.turnOn();
    }

    /** 매개변수에 익명함수 사용해보기 **/
    void method2(Button button){
        button.turnOn();
    }
    
}

 

Main Thread

public class Main {
    public static void main(String[] args) {
    
        Anonymous button = new Anonymous();

        // 1번 방법으로 호출
        button.button.turnOn(); // 위 버튼을 눌러 방 스위치를 킵니다

        // 2번 방법으로 호출
        button.method1();       // O 버튼을 눌러 정답 스위치를 킵니다

        // 3번 방법으로 호출
        // 2번은 Anonymous에서 상세 내용을 작성한 거고 3번 방법은 그걸 밖에서 내가 정의해서 사용하는 차이가 있다.
        // 즉, 어디서 상세 내용을 재정의하냐에 따라 다름
        button.method2(new Button(){

            String name = "엘레베이터";

            // 익명 클래스 안에서만 살아있기 때문에 사용 불가
            void turnOff(){
                System.out.println("엑스 버튼을 눌러 " + name + "를 닫습니다.");
            }

            // 재정의를 했기 때문에 사용 가능
            @Override
            void turnOn(){
                System.out.println("동그라미 버튼을 눌러 " + name + "를 엽니다.");
            }

        }); // 동그라미 버튼을 눌러 엘레베이터를 엽니다.
    }
}

 

 

📝함수형 인터페이스

함수형 인터페이스란 1 개의 추상 메소드를 갖는 인터페이스로 여러 개의 디폴트 메서드가 있더라도 추상 메서드가 오직 하나면 함수형 인터페이스라고 한다 (default method 또는 static method 는 여러 개 존재해도 상관 없다)

매번 함수형 인터페이스를 직접 만들어서 사용하는 건 번거로운 일로서 그래서 Java 에서는 기본적으로 많이 사용되는 함수형 인터페이스를 제공한다 기본적으로 제공되는 것만 사용해도 충분하기 때문에 직접 만드는 경우는 거의 없다.

 

예제 코드

@FunctionalInterface // 함수형 인터페이스 조건에 맞는지 검사
interface CustomInterface<T> {
    
    T myCall(); // abstract method 오직 하나!!

    // default method 는 존재해도 상관없음
    default void printDefault() {
     	System.out.println("Hello Default");
    }

    // static method 는 존재해도 상관없음
    static void printStatic() {
     	System.out.println("Hello Static");
    }
}

 

함수형 인터페이스 장점

  1. 같은 Input에 같은 Ouput을 제공하고 변경 개념이 아니라 복사되고 복사 된 것을 함수를 거쳐 결과를 내기 때문에 멀티스레드 공유자원에 안전하다
  2. 코드를 간결하고 가독성 있게 작성할 수 있다.

 

함수형 인터페이스 단점

  1. 익명 함수의 재사용이 불가능
  2. 재귀함수의 구현이 어렵다.
  3. 재사용이 불가능하므로 비슷한 함수 구현이 잦을 수 있다.
  4. 디버깅이 어렵다.

 

Java에서 제공하는 많이 쓰는 함수형 인터페이스

  1. Runnable           → 파라미터 없고 리턴값도 없는 경우                / 추상메소드 = run()
  2. Consumer<T>  → 파라미터 있고 리턴값 없는 경우                   / 추상메소드 = accept(T t)
  3. Supplier<T>      → 파라미터 없고 리턴값 있는 경우                   / 추상메소드 = get()
  4. Function<T,R>  → 파라미터 있고 리턴값 있는 경우                   / 추상메소드 = apply(T t) // T파라미터, R 반환값
  5. Operator<T>     → 파라미터 있고 리턴값 있는 경우                   / 연산용도, 임의의 타입 전달하고 임의의 타입 반환
  6. Predicate<T>    → 파라미터 있고 리턴값(boolean) 있는 경우  / 추상 메소드 = test(T t)

 

  • Runnable
public class Main {
    public static void main(String[] args) {
    	Runnable runnable = () -> System.out.println("Runnable run");
        runnable.run(); // Runnable run
    }
}

 

  • Consumer
public class Main {
    public static void main(String[] args) {
        Consumer<String> con = text -> System.out.println("parameter : " + text);
        con.accept("Consumer Testing!!");

        /** Consumer Chaining **/
        Consumer<String> cutFruit = fruit -> System.out.println(fruit + "를 깍습니다." ); // Apple를 깍습니다.
        Consumer<String> eatFruit = fruit -> System.out.println(fruit + "를 먹습니다.");  // Apple를 먹습니다.
        cutFruit.andThen(eatFruit).accept("Apple");
    }
}

 

Consumer 파생형

BiConsumer =>  파라미터를 2개 받아서 처리
DoubleConsumer ==> double타입으로 파라미터를 받아서 처리
IntConsumer ==> int타입으로 파라미터를 받아서 처리
LongConsumer ==> long타입으로 파라미터를 받아서 처리
ObjDoubleConsumer ==> 두개의 파라미터중에 하나는 obj 다른하나는 double
ObjIntConsumer ==> 두개의 파라미터중에 하나는 obj 다른하나는 int
ObjLongConsumer ==> 두개의 파라미터중에 하나는 obj 다른하나는 long

 

public class Main {
    public static void main(String[] args) {

        BiConsumer<String, Integer> bi = (text, num) -> System.out.println("파리미터 2개 -> " + text + " + " + num);
        bi.accept("프로그래밍!!", 1234); // 파리미터 2개 -> 프로그래밍!! + 1234
    }
}

 

  • Supplier
public class Main {
    public static void main(String[] args) {
        Supplier<String> supplier = () -> "supplier has return but no params";
        System.out.println(supplier.get()); // supplier has return but no params
    }
}

 

Supplier 파생형

BooleanSupplier :  boolean 타입으로 반환
DoubleSupplier  :  double 타입으로 반환
IntSupplier     :  int 타입으로 반환
LongSupplier    :  long 타입으로 반환

 

  • Operator
public class Main {
    public static void main(String[] args) {
        BinaryOperator<Integer> binaryOperator = (num1, num2) -> num1 + num2;
        int result = binaryOperator.apply(5, 10);
        System.out.println(result); // 15
    }
}

Operator라는 인터페이스는 없다

 

BinaryOperator<T> : BiFunction<T,U,R>  상속 →  두개의 파라미터 전달하고 반환 ( 모두 데이터타입 동일 )

UnaryOperator<T> : Function<T,R> 상속          →  하나의 파라미터 전달하고 반환 ( 모두 데이터타입 동일 )

 

 

Operator 파생형

DoubleBinaryOperator : 두개의 파라미터 double 전달하고  double 반환 ( 모두 데이터타입 동일 )
DoubleUnaryOperator  : 하나의 파라미터 double 전달하고 double반환
IntBinaryOperator    : 두개의 파라미터 int 전달하고  int 반환 ( 모두 데이터타입 동일 )
IntUnaryOperator     : 하나의 파라미터 int 전달하고 int반환
LongBinaryOperator   : 두개의 파라미터 long 전달하고  long 반환 ( 모두 데이터타입 동일 )
LongUnaryOperator    : 하나의 파라미터 long 전달하고 long반환

 

 

  • Predicate
package org.example;

import java.util.function.*;

public class Main {
    public static void main(String[] args) {

        Predicate<String> isNameString = str -> str.equals("name");
        System.out.println("is String Name? test() -> " + isNameString.test("age"));
        // is String Name? test() -> false
        System.out.println("is String Name? test() -> " + isNameString.test("name"));
        // is String Name? test() -> true
    }
}

 

📝 람다식 [익명 객체]

람다는 익명 객체이다 (익명 함수는 함수가 독자적으로 존재해야하지만 Java는 클래스에 종속적이기 때문에 익명 객체라는 말이 맞다)

 

  • 람다식 축약 예제 따라해보기
 class Calculator {

    int sum(int x, int y){
        return x + y;
    } // 람다식 : (x,y) -> x+y;

    void print(String name, int i){
        System.out.println(name + " = " + i);
    } // 람다식 : (name,i) -> System.out.println(name + " = " + i);

    int square(int x){
        return x * x;
    } // 람다식 : x -> x * x

    int roll() {
        return (int) (Math.random() * 6);
    } // 람다식 : () -> (int) (Math.random() * 6);
}
  1. 함수반환 타입을 생략할 수 있다.
  2. 함수명을 생략할 수 있다.
  3. 인자 타입을 생략할 수 있다.
  4. 함수내용인 중괄호 내용이 한줄이면 중괄호는 생략이 가능하다

 

  • 함수형 인터페이스 + 람다식 이용해보기
package org.example;

public class Main {
    public static void main(String[] args) {

        /** 익명 객체 잘못된 사용 방법 **/
        Object object = new Object() {
            int max(int a, int b){
                return a > b ? a : b;
            }
        }; // Object에 max라는게 없기 때문에 object.max(3,5); 사용 불가

        /** 함수형 인터페이스 **/
//        MyFunction myFunction = new MyFunction() {
//            @Override
//            public int max(int a, int b) {
//                return a > b ? a: b;
//            }
//        };

        /** 함수형 인터페이스 + 람다식 **/
        MyFunction myFunction = (a, b) -> a > b ? a : b;
        // 함수형 인터페이스 자체가 추상 메서드가 하나밖에 없기 때문에 
        // 매개변수 및 리턴값에 뭐가 나올지 알고 있어서 생략이 가능하다

        System.out.println(myFunction.max(3,5)); // 5


    }

    interface MyFunction {
        public abstract int max(int a, int b);
    }

}

 

람다식하고 함수형 인터페이스는 짝궁이라고 생각하면 된다.

 

 

 

 

 

 

🔗 참고 및 출처

https://youngest-programming.tistory.com/476

https://ittrue.tistory.com/161

https://limkydev.tistory.com/226

반응형
반응형

 

병렬 작업 처리가 많아지면 스레드 개수가 무한정으로 증가하고 CPU가 바빠지며 메모리 사용량이 늘어난다.

따라서 애플리케이션의 성능이 급격하게 저하되게 된다.

 

스레드 풀

작업 처리에 사용되는 스레드를 제한된 개수 만큼 미리 생성해서 작업 큐에 쌓여지는 작업들을 하나씩 스레드가 맡아서 처리한다. 제한을 걸기 때문에 무한정으로 증가하지 않으며 서버가 뻗지 않게 된다.

 

코어 스레드 수  

최소 스레드 수 (60초 동안 추가된 스레드가 아무 작업 하지 않으면 놀고 있는 스레드가 존재해 없애야한다.)

 

스레드풀의 스레드는 기본적으로 데몬 스레드가 아니기 때문에 main 스레드가 종료되더라도 계속 실행 되므로

반드시 종료시키려면 스레드 풀을 따로 종료시켜야한다.

 

 

 

반응형