이 글은 Java 환경에서 Dynamic Proxy 기반의 기능들을 사용할 때 주로 발생하는 문제인 Self-Invocation을 Lambda를 통해 어떻게 회피할 수 있는지에 대한 내용을 담고 있는 글입니다.

Dynamic Proxy, AOP에 대한 이론적인 부분(발생 원인, 호출 흐름 등)들은 다루지 않을 것이기 때문에 다른 글들을 참고하시길 바랍니다.


Lambda와 관련해서는 학습을 진행하며 작성한 글이기 때문에 잘못된 내용이 있을 수도 있습니다. 틀린 내용이 있다면 채널톡 혹은 댓글을 통해 피드백해주시길 바랍니다. ㅎㅎ


Self Invocation

Self Invocation은 Dynamic Proxy 기반의 기능들을 사용할 때 사소한 실수로 인하여 자주 발생하는 문제입니다. 쉽게 설명하자면, 객체 외부에서 보내는 메시지(요청)에 대해서만 반응하도록 설계되어 있기에 내부의 요청에 대해서는 반응하지 못하기 때문입니다.

JVM 생태계에서 많은 사랑을 받는 Spring Framework는 다양한 기능들을 Dynamic Proxy 방식으로 제공하고 있습니다.

  • @Transcational, @Async, @Cacheable, AOP(Before, Around) 등의 Aspect 기능들이 속합니다.


이러한 기능들에 대한 보편적인 해결 방법으로는

  • AspectJ으로 전환 혹은 부분 적용 (Weaving 방식 전환)
    • @EnableTransactionManagement(proxyTargetClass = true, mode = AdviceMode.ASPECTJ)
  • AopContext의 currentProxy() 메서드를 통해 해당 객체를 감싸고 있는 Proxy 객체를 반환
    • ((Type) AopContext.currentProxy()). someMethod();
  • 상태 변수를 통한 자기 참조 (@Autowired나 ApplicationContext.getBean() 활용)
  • 객체 외부에서 호출하는 메서드에 Dynamic Proxy가 반응하도록 설정하기

등이 있지만, 이것들을 적용하기에는 과한 상황이거나 Spring Container에 종속되는 좋지 않은 코드를 작성하게 될 수 있습니다.


Lambda를 이용하여 Self-Invocation 회피

Lambda로 어떻게 Self-Invocation을 회피할 수 있을까요?

결론만 이야기하자면 Lambda를 통해 실행되는 메서드를 접근하기 위해서 현재 호출 객체 외부로 메시지가 나가고, 최종적으로 호출해야 되는 메서드를 찾아 요청하였을 때, 외부에서 전달되기 때문에 감싸고 있는 Proxy가 해당 요청을 인지할 수 있기 때문입니다.

Java Lambda는 Reflection API, MethodHandle, LambdaMetaFactory 인터페이스를 이용하여 기능을 제공합니다.


Lambda Method를 호출하는 흐름

  1. Reflection API를 통해 실행 대상이 되는 메서드 정보를 가져옵니다.
  2. MethodHandle Lookup API에 정의된 Factory 메서드를 통해 Lookup 객체를 가져옵니다.
  3. 1번에서 가져온 정보를 Lookup.unreflect() 메서드에 전달함으로써 해당 메서드의 구현, 수행 정보를 알고 있는 MethodHandle 객체를 가져옵니다. (실제 메서드를 바라보고 있는 일종의 포인터)
  4. LambdaMetafactory.metafactory() 메서드에 필요한 인자를 넘겨 CallSite 객체를 반환받습니다. 해당 객체는 Functional Interface를 객체로 다룰 수 있으며, 매개 변수를 설정하고 응답을 반환합니다. 인자 목록은 밑에 나열하였습니다.
    1. 접근 권한을 가지고 있는 Lookup 객체
    2. 구현할 메서드 이름(Supplier Interface를 사용했을 경우 get이라는 문자열을 넘긴다.)
    3. 메서드의 매개 변수와 응답 값의 Class 정보. methodType(Supplier.class, {Type}. class)
    4. 함수 객체(Lambda)에 의해 반환될 응답 값의 유형. methodType(Object.class)
    5. 메서드의 구현 및 수행 방식을 알고 있는 MethodHandle 객체
    6. 호출 시 동적으로 적용되어야 할 응답 값의 세부 정보. methodType({Type}. class)
  5. callSite.getTarget()을 통해 호출할 메서드 정보를 가져오고 bindTo({Type}. class)를 통해 메서드에 넘길 인자 값을 지정한 뒤 Invoke를 통해 메서드를 호출합니다.

(사실상 그 호출하는 형태는 Dynamic Proxy와 유사한 것 같습니다)

꽤 복잡하지만, 결론적으로는 Lambda를 통해 호출되는 인터페이스를 인스턴스 화 하고, 메서드를 호출하기 때문에 객체 외부 요청으로 다시 돌아오는 것입니다.

  • 이 흐름은 Bytecode의 invokedynamic이 호출된 경우에 수행 흐름을 나타냅니다. Lambda에서 Function Interface를 사용하지 않고 단순한 로직을 사용하는 경우 static 메서드를 생성하여 활용하기도 합니다.
    • invokedynamic은 Bootstrap 메서드라는 특정한 메서드를 호출하고, 이를 통해 위의 호출 프로세스를 초기화 및 실행하여 CallSite 객체를 반환받습니다. (InnerClassLambdaMetafactory를 활용하여 내부 클래스 생성 후 반환) 
  • 한번 Bootstrap 메서드가 실행된다면 이후에는 기존의 CallSite와 MethodHandle를 재사용하여 요청을 처리합니다.

 

 

 

구현 예시

Self-Invocation을 회피하기 위해 구현한 TransactionalHandler입니다.

@Service
public class TransactionHandler {

    @Transactional(propagation = Propagation.REQUIRED)
    public <T> T runInTransaction(Supplier<T> supplier) {
        return supplier.get();
    }
}


Sample Service.

@Service
@RequiredArgsConstructor
public class SampleService {

    private final SampleRepository someRepository;
    private final TransactionHandler transactionHandler;

    // 특정 객체에서 호출하는 Method
    public void addNewSamples(List<Sample> samples) {
        return samples.forEach(sample ->
            transactionHandler.runInTransaction(() -> addSample(sample.toEntity()))
        )
    }

    // 외부에서 호출되는 Method
    @Transcational
    public SomeEntity addSample(SampleEntity newSample) {
        return someRepository.insertSample(newSample);
    }
}


단순한 예시여서 실제 효용성과 조금 동떨어진 감은 있지만, 실제 업무 중 활용할 수 있을만한 부분을 특정하실 수 있을 것이라고 생각합니다. ㅎㅎ 이 글은 여기까지입니다. 감사합니다.


참고 자료

3주 차 시작합니다!

연산이란?

주어진 정보를 통해 일정한 규칙에 따라 어떤 값이나 결과를 구하는 과정을 의미한다.

 

연산자란?

연산을 진행하는 동안 사용되는 기호를 말한다.

연산 대상의 수나 연산 방식에 따라서 명칭이 나뉘게 된다.

 

피연산자란?

연산될 대상을 말하며. 변수, 상수, 리터럴 등을 의미한다.

 

단항 연산자 (Unary Operator)

연산이 수행될 피연산자가 1개인 경우 해당 연산자를 단항 연산자라고 한다.

  • 전위 증감, 후위 증감 연산자
  • 단항으로써 사용하는 +, - 연산자 (부호 연산자)
  • 비트 반전 ~ 연산자

이항 연산자 (Binary Operator)

연산이 수행될 피연산자가 2개인 경우 해당 연산자를 이항 연산자라고 한다.

  • 산술 연산자
  • 비트 연산자
  • 관계 연산자
  • 대입 연산자
  • 논리 연산자

삼항 연산자 (Ternary Operator)

연산이 수행될 피연산자가 3개인 경우 해당 연산자를 삼항 연산자라고 한다.

  • 조건 연산자 ( 조건식? True의 경우 반환될 식 : False의 경우 일시 반환될 식 )

 

산술 연산자 (Arithmetic Operator)

산술 연산자는 수치 계산을 실시하는 연산자를 말한다.

  • A + B : 덧셈 연산자 (정수형, 실수형 리터럴뿐만 아니라 문자열 연산에도 사용 가능하다.)

      // 기본적인 사용법
          int numA = 10;
          int numB = 10;
    
          System.out.println(numA + numB); // 20
    
          int i = numA + numB;
        System.out.println(i);
    • 문자열 더하기 연산

      String strA = "hello";
      strA += " World";
      System.out.println(strA); // hello world
    • 값의 범위가 다른 Type 간의 연산 (공통)

      // long과 int를 더하는 경우에는?
      long longA = 10l;
      int numC = 10;
      
      System.out.println(longA+numC);
      
      // 실제로는 int 가 long으로 Type Conversion이 된 것을 알 수 있다. - 2주차 내용
      System.out.println(longA + (long)numC);
      
      // long과 double를 더하는 경우에는?
      long longA = 10;
      double doubleA = 10.0;
      
      System.out.println(longA+doubleA);
      
      // 실제로는 long 이 double로 Numeric Promotion이 된 것을 알 수 있다.
      System.out.println((double)longA + doubleA);
      
  • A - B : 뺄셈 연산자

      int numA = 10;
      int numB = 10;
    
      System.out.println(numA - numB); 
    
      int j = numA - numB; 
      System.out.println(j); // 0 출력
  • A / B : 나눗셈 연산자

    나눗셈 연산과 나머지 연산의 경우 피연산자로 0을 사용하면 Exception이 발생한다.

      int numA = 10;
      int numB = 10;
    
      System.out.println(numA / numB); 
    
      int k = numA / numB;
      System.out.println("k = " + k); // 1 출력 
  • A % B : 나머지 연산자

      int numA = 11;
      int numB = 2;
    
      System.out.println(numA % numB); 
    
      int l = numA % numB;
      System.out.println("l = " + l); // 1 출력
  • A * B : 곱하기 연산자

      int numA = 11;
      int numB = 2;
    
      System.out.println(numA * numB); 
    
      int h = numA % numB;
      System.out.println("h = " + h); // 22 출력
  • ++N, --N : 전위 증감 연산자

      int numC = 10;
      numC = --numC;
      System.out.println("numC = " + numC);
    
      int numD = 10;
      numD = ++numD;
      System.out.println("numD = " + numD);
    
      // Decompile
      // 전위 증감 연산자는 내부적으로 A = A +- 1; 의 형식을 취하는 것을 알 수 있다.
    
      int numC = 10;
      int numC = numC - 1;
      System.out.println("numC = " + numC);
    
      int numD = 10;
      int numD = numD + 1;
      System.out.println("numD = " + numD);
    
  • N++, N-- : 후위 증감 연산자

      int numE = 10;
      System.out.println(numE--);
      System.out.println("numE = " + numE);
    
      int numF = 10;
      System.out.println(numF--);
      System.out.println("numF = " + numF);
    
      // Decompile
      // 우선 임시변수를 만들어서 해당 값을 복사한뒤 전달하고 반영된 값을 그 이후에 제공한다.
    
      int numE = 10;
      byte var10001 = numE; // 임시변수에 값 복사
      int numE = numE - 1; // 변수 연산
      System.out.println(var10001); // 임시변수 출력
      System.out.println("numE = " + numE); // 연산된 변수를 제공
    
      int numF = 10;
      var10001 = numF; 
      int numF = numF - 1;
      System.out.println(var10001);
      System.out.println("numF = " + numF);
    • Wrapper와 Primitive 간의 산술 연산

    • Wrapper Class* (Integer, Long, Double)와 Primitive Type 연산 시에는 Wrapper Class 피연산자가 Unboxing 되어 연산이 진행되고 다시 Boxing이 되는 등에 성능상 오버헤드가 발생한다.

      int numB = 10;       
      
      Integer numA = numB; //autoboxing
      
      numA = numA + numB; //unboxing 후 연산 -> 결과 반영 전 다시 autoboxing 진행

      그리고 Operand Stack에서 관리되는 Primitive TypeHeap에서 참조되는 Wrapper Type은 값에 접근하는 시간에 차이가 존재한다. (일반적으로 Wrapper의 Access 시간이 길다.)

    • Primitive 도 일정 값 이상의 경우, Constant Pool이라는 Stack 외부에 존재하는 공간에 저장된다.*

    • JVM 설정 중에 Primitive 값의 크기에 대한 설정이 있다고 한다.

    • 즉 프로그램의 환경적인 부분에 따라서 수치나 연산 시간 등이 달라질 수 있다.

 

비트 연산자?

  • A & B : AND 연산 ( A와 B의 비트에 대해서 AND 연산을 수행한다.)

      //양쪽의 비트가 1이면 1, 0이면 0, 0 과 1 인 경우에는 0을 나타낸다.
    
      int num = 15; // 1111
      int nums = 6; // 0110
                    // 0110 : AND 연산
    
      int i = num & nums;
      System.out.println("i = " + i); // 6
  • A | B : OR 연산 ( A와 B의 비트에 대해서 OR 연산을 수행한다.)

      //하나의 비트가 1이면 1, 양쪽의 비트가 0이면 0이다.
    
      int num = 15; // 1111
      int nums = 6; // 0110
                    // 1111 : OR 연산
    
      int i = num | nums;
      System.out.println("i = " + i); // 15
  • A ^ B : XOR 연산 ( A와 B의 비트에 대해서 XOR 연산을 수행한다.)

      //하나의 비트가 1이면 1, 양쪽의 비트가 0이면 0, 양쪽의 비트가 1이면 0이다.
    
      int num = 15; // 1111
      int nums = 6; // 0110
                    // 1001 : XOR 연산
    
      int i = num ^ nums;
      System.out.println("i = " + i); // 9
  • A << N : 시프트 연산자 ( A의 비트를 좌측으로 N만큼 이동한다.)

      //비트를 좌측으로 N만큼 이동하고 우측에서 N만큼의 영역을 0으로 채우게된다.
    
      int num = 15; // 1111
      int nums = 2;
    
      int i = num << nums; // 111100 = 좌측으로 비트를 2칸씩 이동.
      System.out.println("i = " + i);
  • A >> N : 시프트 연산자 ( A의 비트를 우측으로 N만큼 이동한다.)

      //비트를 우측으로 N만큼 이동하고 좌측에서 N만큼의 영역을 0으로 채우게된다.
    
      int num = 15; // 1111
      int nums = 2;
    
      int i = num >> nums; // 0011 = 우측으로 비트를 2칸씩 이동.
      System.out.println("i = " + i);
  • A >>> N : 부호가 없는 시프트 연산자 ( A의 2진수를 우측으로 비트를 N만큼 이동한다.)

      int num = -15; // 11111111111111111111111111110001
      int nums = 2;
    
      // 부호를 신경쓰지 않고 모든 비트 값들을 오른쪽으로 이동시킨 후에 왼쪽의 빈 공간은 모두 
      // 0 으로 채운다.
    
      int i = num >>> nums; 
      System.out.println("i = " + i); // 1073741820 = 00111111111111111111111111111100
    
      int j = -15 >> nums;         
      System.out.println("j = " + j); // -4 : 11111111111111111111111111111100 
  • ~A : 반전 연산자 ( A 내부의 비트 값을 반전시킨다.)

      int num = -15; // 11111111111111111111111111110001
    
      System.out.println(~num);   // 14 : 00000000000000000000000000001110

 

관계 연산자?

  • A == B : 동일성 비교 ( A와 B가 같다면?)

      int numA = 10;
      int numB = 10;
    
      boolean isSame = numA == numB; // boolean 값을 반환.
    
      if (isSame){
          System.out.println(isSame); // true
      }
    
      혹은
    
      if (numA == numB){
          System.out.println(isSame); // true
      }
  • A != B : 동일하지 않은 경우 ( A와 B가 같지 않다면?)

      int numA = 10;
      int numB = 9;
    
      boolean isSame = numA != numB;
    
      if (isSame){
          System.out.println(isSame); //true
      }
    
      또는
    
      if (numA != numB){
          System.out.println(isSame); //true
      }
    
      또는
    
      boolean isSame = numA == numB;
    
      if (!isSame){
          System.out.println(isSame); //false 
      }
  • A > B : 대소 관계 비교 ( A가 B보다 크다면?)

      int numA = 10;
      int numB = 9;
    
      boolean isSame = numA > numB;
    
      if (isSame){
         System.out.println(isSame); //true
      }
    
      if (numA > numB){
         System.out.println(isSame); //true
      }
  • A >= B : 대소관계 비교 ( A가 B보다 크거나 같다면?)

      int numA = 10;
      int numB = 10;
    
      boolean isSame = numA >= numB;
    
      if (isSame){
         System.out.println(isSame); //true
      }
    
      if (numA >= numB){
         System.out.println(isSame); //true
      }
  • A < B : 대소관계 비교 ( A가 B보다 작다면?)

      int numA = 9;
      int numB = 10;
    
      boolean isSame = numA < numB;
    
      if (isSame){ 
         System.out.println(isSame); //true
      }
    
      if (numA < numB){
         System.out.println(isSame); //true
      }
  • A <= B : 대소관계 비교 ( A가 B보다 작거나 같다면?)

      int numA = 10;
      int numB = 10;
    
      boolean isSame = numA <= numB;
    
      if (isSame){
         System.out.println(isSame); //true
      }
    
      if (numA <= numB){
         System.out.println(isSame); //true
      }

 

논리 연산자?

  • 조건식 1 && 조건식2 : 조건식1과 조건식 2를 모두 만족한다면?

      int numA = 10;
      int numB = 10;
      int numC = 10;
      int numD = 10;
    
      boolean bool = numA == numB && numC < numD; //false
    
      if (bool){
         System.out.println(bool);
      }else {
           **System.out.println(bool);
      }
    
      if (numA == numB && numC == numD){
         System.out.println(numA == numB && numC == numD); //true
      }
  • 조건식1 || 조건식2 : 조건식1 혹은 조건식2를 만족한다면?

      int numA = 10;
      int numB = 10;
      int numC = 10;
      int numD = 10;
    
      boolean bool = numA == numB || numC < numD; //true
    
      if (bool){
         System.out.println(bool); 
      }else {
         System.out.println(bool);
      }
    
      if (numA == numB || numC == numD){
         System.out.println(numA == numB && numC == numD); //true
      }
  • ! : 논리 반전 연산자 : != 같지않다면?, !< 작지않다면?

      int num = 10;
      String conn = null;
    
      boolean bool = num != 9;
    
      if (bool){
         System.out.println(bool);
      }else {
         System.out.println(bool);
      }
    
      if (conn != null){
         System.out.println("true");
      }else {
         System.out.println("false");
      }
  • JDK Assertion (JDK 1.4+)

    이전에 작성된 코드에서 사용된 assert라는 변수들과의 하위 호환성을 위해 기본적으로 비활성화되어 있다.

    ```java
    assert service != null; // true -> 아무 일도 발생하지 않는다.
                                                // false -> AssertionError (Unchecked Exception) 발생
    
    혹은
    
    assert service != null : "Service is Null"; // false 시 추가적으로 메세지를 제공한다.

 

instanceof

  • A instanceof B : (공변) 타입 검증 연산자, A가 B의 타입 혹은 하위 구현체인지 판단한다.
    • Reference Type 만 사용 가능하다.
public static void main(String[] args) {
    InstanceofExample example = new InstanceofExample();
    example.run();
}

public void run() {
    SomeClass someClass = new SomeClass();
    SomeClasz someClasz = new SomeClasz();
    SomeClassChild someClassChild = new SomeClassChild();

    typeCheck(someClass);      // is SomeClass
    typeCheck(someClasz);      // is SomeClasz
    typeCheck(someClassChild); // is SomeClass
}

public void typeCheck(Object obj){
    if (obj instanceof SomeClass){
        System.out.println("is SomeClass");
    }else if (obj instanceof SomeClasz){
        System.out.println("is SomeClasz");
    }
}

class SomeClass {

}
class SomeClassChild extends SomeClass{

}
class SomeClasz {

}

 

대입 연산자? (assignment(=) operator)

Primitive 변수에 값을 할당하기 위해 사용하거나 Reference의 참조 값을 할당하기 위해 사용한다.

  • 그 외에도 이항 연산자와 결합하여 사용할 수 있다.
int num = 4;
int variable;

variable = num; // variable = 4;
System.out.println("= 연산 : " + variable); // = 연산 : 4

variable += num; // variable = 4 + 4
System.out.println("+= 연산 : " + variable); // += 연산 : 8

variable -= num; // variable = 8 - 4
System.out.println("-= 연산 : " + variable); // -= 연산 : 4

variable *= num; // variable = 4 * 4
System.out.println("*= 연산 : " + variable); // *= 연산 : 16

variable /= num; // variable = 16 / 4
System.out.println("/= 연산 : " + variable); // /= 연산 : 4

variable %= num; // variable = 4 % 4 
System.out.println("%= 연산 : " + variable); // %= 연산 : 0

variable = 10;
variable &= 2; // variable = 10 & 2
System.out.println("&= 연산 : " + variable); // &= 연산 : 2

variable = 10;
variable |= 2; // variable = 10 | 2
System.out.println("|= 연산 : " + variable); // |= 연산 : 10

variable = 10;
variable ^= 2; // variable = 10 ^ 2
System.out.println("^= 연산 : " + variable); // ^= 연산 : 8

variable = 10;
variable <<= 2; // variable = 10 << 2
System.out.println("<<= 연산 : " + variable); // <<= 연산 : 40

variable = 10;
variable >>= 2; // variable = 10 >> 2
System.out.println(">>= 연산 : " + variable); // >>= 연산 : 2

variable = 10;
variable >>>= 2; // variable = 10 >>> 2
System.out.println(">>>= 연산 : " + variable); // >>>= 연산 : 2

 

Arrow operator? ( (parameter list) -> lambda body )

Java 8에 추가된 lambda 식을 지원하기 위해 사용되는 연산자이다.

// 이러했던 구문을
Runnable runnable = new Runnable() {
   @Override
   public void run() {
       System.out.println("Hello!");
   }
};

// 이렇게 축약할 수 있다.

// 하나의 식, 로직을 가지고 있다면 { } block 과 return도 생략 가능하다.
Runnable runnable = () -> System.out.println("Hello!");

// 그렇지 않다면 이런식으로 작성해야한다.
Runnable runnable = () -> {
    System.out.println("Hello!");
    helloLogic();
**};**

 

3항 연산자? ( Ternary Operator : 변수 = (조건)? true 인 경우 : false 인 경우)

삼항 연산자는 피연산자 3개를 사용하며, if-then-else 명령문의 축약형이라고 할 수 있다.

int num = 10;
String result;

if(num == 10){
        result = "true";
        System.out.println("result = " + result);
}else {
        result = "false;
        System.out.println("result = " + result);
}

result = (num == 10) ? "true" : "false";
System.out.println("result = " + result);

 

연산자 우선순위?

수식 내에 여러 연산자가 함께 등장할 때, 어느 연산자가 먼저 처리될 것인가를 결정합니다.

( ) 등으로 우선순위, 결합 방향에 따른 연산자의 처리 순서를 변경할 수도 있습니다.

 

연산자의 결합 규칙?

수식 내에 우선순위가 같은 연산자가 둘 이상 있을 때, 먼저 어느 연산을 수행할 것인가를 결정합니다.

int numA = 5;
int numB = 2;
int numC = 2;

// +, - 의 우선 순위가 동일하다.
// 결합 방향에 따라서 왼쪽부터 오른쪽으로 진행하게 된다.
int res = numA + numB - numC;

 

Java 13의 switch 연산자?

기존 switch 문법으로 만든 요일 반환식 (윤년을 앞선 로직에서 체크했다고 가정.)

public static String monthCheck(int num){
        int days = 0;
        switch (num) {
            case 1 :
            case 3 :
            case 5 :
            case 7 :
            case 8 :
            case 10 :
            case 12 :
                days = 31;
                break;
            case 4 :
            case 6 :
            case 9 :
            case 11 :
                days = 30;
                break;
            case 2 :
                days = 28;
                break;
            default:
                days = -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

Java 12에서 switch는

  • case 라벨을 쉼표로 구분하여 사용하게끔 문법을 지원하게 되었다.
  • break을 통해 값을 반환할 수 있게끔 되었다.
public static String monthCheck(int num){
        int days = switch (num) {
            case 1, 3, 5, 7, 8, 10, 12 :
                break 31;
            case 4, 6, 9, 11 :
                break 30;
            case 2 :
                break 28;
            default:
                break -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

혹은 이와 같이 람다 식을 사용할 수 있게 되었다.

public static String monthCheck(int num){
        int days = switch (num) {
            case 1, 3, 5, 7, 8, 10, 12 -> 31;
            case 4, 6, 9, 11 -> 30;
            case 2 -> 28;
            default -> -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

Java 13에서 switch는

  • 기존에 사용하던 break이라는 키워드를 yield로 대체(확장) 하게 되었다.
public static String monthCheck(int num){
        int days = switch (num) {
            case 1, 3, 5, 7, 8, 10, 12 :
                yield 31;
            case 4, 6, 9, 11 :
                yield 30;
            case 2 :
                yield 28;
            default:
                yield -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

또는

public static String monthCheck(int num){
        int days = switch (num) {
            case 1, 3, 5, 7, 8, 10, 12 -> 31;
            case 4, 6, 9, 11 -> 30;
            case 2 -> 28;
            default -> {
                                System.out.println("잘못 입력했습니다.");
                                yield -1;
                        }
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

이렇게 표현할 수 있다.

 

Java 12 에서는 해당 기능을 미리보기 기능으로 제공하였고, (활성화 필요)

Java 13 에서는 해당 식을 확장하였으며,

Java 14 에서는 해당 기능을 표준으로 제공하게 되었다.

 

 

참고 자료

+ Recent posts