3주차 - 연산자

Hwan·2022년 1월 13일
0
post-thumbnail

목표

자바가 제공하는 다양한 연산자를 학습하세요.

학습할 것

  • 산술 연산자
  • 비트 연산자
  • 관계 연산자
  • 논리 연산자
  • instanceof
  • assignment(=) operator
  • 화살표(->) 연산자
  • 3항 연산자
  • 연산자 우선 순위
  • (optional) Java 13. switch 연산자

연산자

  • 연산자(operator) : 연산을 수행하는 기호 (+,-,* 등)
  • 피연산자(perand) : 연산자의 작업 대상 (변수, 상수, 리터럴, 수식)

연산자의 종류

피연산자의 개수에 의한 분류

  • 단항 연산자
    • 연산이 수행될 피연산자가 1개인 경우
      • 전위 증감, 후위 증감 연산자
      • 단항으로써 사용하는 +, -연산자 (부호 연산자)
      • 비트 반전 ~ 연산자
  • 이항 연산자
    • 연산이 수행될 피연산자가 2개인 경우
      • 산술 연산자
      • 비트 연산자
      • 관계 연산자
      • 대입 연산자
      • 논리 연산자
  • 삼항 연산자
    • 연산이 수행될 피연산자가 3개인 경우
      • ?:

1. 산술 연산자

사칙 연산자(+ - * /)

덧셈(+), 뺄셈(-), 곱셈(*), 나눗셈(/)

우선순위도 우리가 알고 있듯이 곱셈, 나눗셈이 덧셈, 뺄셈 연산자보다 높다.

public static void main(String args[]) {
		int a = 10;
    int b = 4;
    System.out.printf("%d + %d = %d%n", a, b, a + b);
    System.out.printf("%d - %d = %d%n", a, b, a - b);
    System.out.printf("%d * %d = %d%n", a, b, a * b);
    System.out.printf("%d / %d = %d%n", a, b, a / b);//---(1)
    System.out.printf("%d / %f = %f%n", a, (float)b, a / (float)b); //---(2)
}

==========================================
10 + 4 = 14
10 - 4 = 6
10 * 4 = 40
10 / 4 = 2
10 / 4.000000 = 2.500000

(1) : 10 / 4의 출력값으로 2.5가 아닌 2가 나오게 되었다. 이유는 자료형에 있다. 연산에 사용된 두 피연산자는 모두 int타입이다. 고로 연산결과 역시 int타입이기 때문에 실제 연산 결과가 2.5일지라도 int타입의 경우 소수점을 저장하지 못하므로 정수만 남고 소수점 이하는 버려져 2가 반환되게 된다.

그래서 올바른 연산결과를 얻기 위해서는 두 피연산자 중 어느 한 쪽을 실수형으로 형변환을 해야한다. 그래야만 다른 한 쪽도 같이 실수형으로 자동 형변환되어 결국 실수형의 값을 결과로 얻게 된다.

(2): 두 피연산자의 타입이 각각 int타입과 float타입으로 일치하지 않기 때문에 int타입보다 범위가 넓은 float타입으로 일치시킨 후에 연산을 수행한다.

피연산자가 정수형인 경우, 나누는 수로 0을 사용하게 될 경우ArithmeticException가 발생한다.

또한, 부동 소수점 값인 0.0f, 0.0d 로 나누는 것은 가능하지만 그 결과는 Infinity(무한대)이다.

  • NaN은 숫자가 아니라는 뜻이다.

작은 범위로의 형변환

byte a = 10;
byte b = 30;
byte c = (byte)(a*b);
System.out.println(c);
============================
44

큰 자료형의 값을 작은 자료형의 변수에 저장하려면 명시적 형변환을 해줘야 한다.

대신, 데이터의 손실이 발생하므로 값이 바뀔 수 있다.

오버플로우

public static void main(String[] args) {
    int a = 1_000_000;
    int b = 2_000_000;

    long c = a * b; // a * b = 2,000,000,000,000 ?

    System.out.println(c);
}

======================================================
-1454759936

c의 타입이 long타입이기 때문에 2 x 10¹²을 저장하기에 충분하므로 정상적으로 출력이 될 것 같지만, 결과는 전혀 다른 값이 출력된다. 그 이유는 int타입과 int타입의 연산결과는 int타입이기 때문에 a*b의 결과가 이미 오버플로우가 발생하여 long타입으로 자동 형변환되어도 값은 변하지 않는다.

그렇기에 해당 연산이 진행되기 전 하나의 피연산자를 충분한 크기의 자료형으로 형변환해서 타입일치를 시켜 충분한 범위를 확보해야 한다.

public static void main(String[] args) {
    int a = 1_000_000;
    int b = 2_000_000;

    long c = (long)a * b; 

    System.out.println(c);
}

======================================================
2000000000000

Tip. 가운데 값 구하기

public static void main(String[] args) {
    int start = 2_000_000_000;
    int start = 2_100_000_000;

    int mid = start + (end - start) / 2; 
		//int mid = (start + end) >>> 1; 도 가능하다. 
		//unsigned right shift -> 오른쪽으로 이동 후 남아 있는 빈 비트 공간을 0으로 채움. (음수 X)
    System.out.println(mid);
}
  • 위에 말한 것 처럼 (start+end) / 2 로 가운데 값을 구해버린 다면 오버플로우가 발생할 수 있으니 start값에 (end - start) / 2의 값을 더해주는 방향으로 하는 것이 더욱 안전하다.

문자의 산술연산

출처) https://whatisthenext.tistory.com/103

사칙연산의 피연산자로 숫자뿐만 아니라 문자도 가능하다. 문자는 실제로 해당 문자의 유니코드(부호없는 정수)로 바뀌어 저장되기 때문이다.

System.out.println('d' - 'a'); // 3

리터럴 간의 연산

char c = 'a' + 1; //b 

위 코드는 오류가 발생하지 않고 실행도 올바른 결과를 얻게 된다. int보다 작은 타입의 피연산자를 int타입으로 자동 형변환한다고 배웠는데 왜 문제가 없는걸까? 이는 리터럴 간의 연산이기 때문이다.

상수 또는 리터럴 간의 연산은 실행 과정동안 변하는 값이 아닌 컴파일 시에 컴파일러가 계싼해서 그 결과를 대체하기 때문에 컴파일 후에는 이미 char c = 'b';가 되어있기 때문에 덧셈 연산이 수행되지 않는다.

하지만 수식에 변수가 들어가 있는 경우 컴파일러가 미리 계산을 할 수 없기 때문에 명시적 형변환을 해줘야 한다.

나머지 연산자 %

나머지 연산자는 왼쪽의 피연산자를 오른쪽 피연산자로 나누고 난 나머지 값을 결과로 반환하는 연산자이다.

나눗셈에서처럼 나누는 수(오른쪽 피연산자)로 0을 사용할 수 없다.

2. 비트 연산자

비트 연산자는 피 연산자를 비트단위로 연산하는데, 피 연산자를 이진수로 표현했을 때의 각 자리를 규칙에 따라 연산을 수행하며, 피연산자로 실수는 허용하지 않으며 정수만 허용된다.

int a = 3 & 1; // 0011 & 0001 = 0001(1) 
int b = 2 | 1; // 0010 | 0001 = 0011(3)
int c = 3 ^ 1; // 0011 ^ 0001 = 1110(2)
// 2진수를 4자리로 표현하였지만 int타입간의 연산이라 32자리로 표현하는 것이 맞다.
  • & (AND)
    • 피연산자 중 한 쪽의 값이 1이면, 1을 결과로 얻는다. 그 외에는 0을 얻는다.
    • 주로 특정 비트의 값을 변경할 때 사용한다.
  • | (OR)
    • 피연산자 양 쪽이 모두 1일때만 1을 결과로 얻는다. 그외에는 0을 얻는다.
    • 주로 특정 비트의 값을 추출할 때 사용한다.
  • ^ (XOR)
    • 피연산자의 값이 서로 다를때만 1을 결과로 얻는다. 그외에는 0을 얻는다.
    • 같은 값으로 두고 XOR 연산을 수행하면 원래값으로 돌아오기에 간단한 암호화에 사용한다.

비트 전환 연산자 ~

int d = ~10; //00001010(10) -> 11110101(-11)
// 2진수를 8자리로 표현하였지만 int타입간의 연산이라 32자리로 표현하는 것이 맞다.
  • ~ (NOT)
    • 피연산자를 2진수로 표현했을 때 0은 1로 1은 2로 바꾼다.
    • 1의 보수 연산자라고도 한다.

이러한 비트 전환연산자는 음수를 표현하기 위해 사용되는데 음수를 표현할 수 없는 컴퓨터의 제한적인 상황을 1의 보수를 통해 해결한 것이다.

쉬프트 연산자 >> <<

int e = 8 >> 2; // 00001000(8) -> 00000010(2)
int f = 8 << 2; // 00001000(8) -> 00100000(32)
  • >> (right SHIFT)
    • 피연산자의 각 자리(2진수로 표현했을 때)를 오른쪽으로 이동한다.
    • 오른쪽으로 이동시키기 때문에 부호있는 정수는 부호를 유지하기 위해 왼쪽 피연산자가 음수인 경우 빈자리를 1로 채우고 양수일 때는 0으로 채운다.
  • << (left SHIFT)
    • 피연산자의 각 자리(2진수로 표현했을 때)를 왼쪽으로 이동한다.
    • 피연산자의 부호에 상관없이 각 자리를 왼쪽으로 이동시키며 빈칸을 0으로만 채운다.

x << n은 x * 2n2^n의 결과와 같다.

x >> n은 x / 2n2^n의 결과와 같다.

3. 관계 연산자

주로 조건문과 반복문의 조건식에 사용되며, 연산결과는 오직 truefalse 둘중 하나이다.(boolean)

관계 연산자 역시 이항 연산자이므로 비교하는 피연산자의 타입이 서로 다를 경우에는 자료형의 범위가 큰 쪽으로 타입을 일치시킨 뒤 비교한다.

대소 비교 연산자

두 피연산자의 값의 크기를 비교하는 연산자로 참일 경우 true, 아닐 경우 false를 반환한다.

기본형인 boolean형을 제외하고 다 사용가능하지만 참조형에는 사용할 수 없다.

System.out.println(2 > 1); // true 
System.out.println(7 < 4); // false
System.out.println(2 >= 2); // true 
System.out.println(4 <= 3); // false 
  • > : 좌변 값이 크면 true 아니면 flase
  • < : 좌변 값이 작으면 true 아니면 flase
  • >= : 좌변 값이 크거나 같으면 true 아니면 flase
  • <= : 좌변 값이 작거나 같으면 true 아니면 flase

등가비교 연산자

두 피연산자의 값이 같은지 또는 다른지를 비교하는 연산자이다. 대소비교 연산자와는 다르게 참조형을 포함하여 모든 자료형에서 사용이 가능하다. 참조형의 경우에는 객체의 주소값을 저장하고 있기에 해당 주소값을 비교하여 값을 비교할 수 있다.

기본형과 참조형은 서로 형변환이 가능하지 않기 때문에 등가비교 연산자로 기본형과 참조형을 비교할 수는 없다.

class Animal{
 ...
}

Animal animal = new Animal();
Animal animalSecond = animal;

System.out.println(2 == 2); // true 
System.out.println(4 != 4); // flase 
System.out.println(animal == animalSecond); // true
  • = : 두 값이 같으면 true 아니면 false
  • != : 두 값이 다르면 true 아니면 false
System.out.println(10.0 == 10.0f); // true ---(1)
System.out.println(0.1 == 0.1f); // false  ---(2)

(1) : 관계 연산자도 이항 연산자이므로 연산을 수행하기 전에 형변환을 통해 두 피연산자의 타입을 같게 맞춘 다음 피연산자를 비교한다. 10 == 10.0f에서 더 범위가 넓은 자료형인 float으로 10을 변환한 뒤 비교한다.

(2) : (1)과 달리 결과가 false가 나왔다. 왜 이런 결과를 얻는 걸까? 그것은 정수형과 달리 실수형은 근사값으로 저장되므로 오차가 발생할 수 있기 때문이다. 10.0f는 오차없이 저장할 수 있는 값이라서 double로 형변환해도 그대로 10.0이 되지만, 0.1f는 저장할 때 2진수로 변환하는 과정에서 오차가 발생한다. 물론 double도 오차가 발생하지만 float 타입의 0.1f보다 적은 오차로 저장된다.

float f = 0.1f; // 0.10000000149011612로 저장된다.
double d = 0.1; // 0.10000000000000001로 저장된다.

그렇다면 어떻게 실수형을 비교해야 할까?

  1. double타입의 값을 float형변환한 다음 비교한다.
  2. 어느 정도의 오차는 무시하고 두 타입의 값을 앞에서 몇자리만 절삭하여 비교한다.

4. 논리 연산자

논리 연산자

x > 10 && x < 20 // x는 10보다 크고 20보다 작다. 
i % 2 == 0 || i % 3 == 0 // i는 2의 배수 또는 3의 배수이다.
  • || (OR 결합) : 피연산자 중 어느 한쪽만 true이면 true를 결과로 얻는다.
  • && (AND 결합) : 피연산자 양쪽 모두 true이어야 true를 결과로 얻는다.

논리 부정 연산자

ch < 'a' || ch > 'z' <-> !('a' <= ch && ch <= 'z')
  • ! : 피연산자가 true이면 false를, false면 true를 결과로 반환한다.

5. instanceof

참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 사용한다.

참조변수 instanceof 타입(클래스)

반환 타입은 Boolean이며 반환 값이 true일 경우 참조변수가 검사한 타입으로 형변환이 가능하다는 의미가 된다.

public class Car {
	...
}

public class FireEngine extends Car{
    void water(){
        System.out.println("물 뿌리기");
    }
}

public class Ambulance extends Car{
    void siren(){
        System.out.println("웨옹 웨옹");
    }
}

public class Test {
    public static void main(String[] args) {
        Car car = new FireEngine();
        doShout(car);
    }

    static void doWork(Car car) {
        if (car instanceof FireEngine) {
            FireEngine fireEngine = (FireEngine)car;
            fireEngine.water();
        }

        if (car instanceof Ambulance) {
            Ambulance ambulance = (Ambulance)car;
            ambulance.siren();
        }
    }
}

==================================================
물 뿌리기

위 코드에서 doWork 는 Car타입의 참조변수를 매개변수로 하는 메서드이다. 이 메서드가 호출될 때, 매개변수로 Car클래스 또는 그 자식 클래스의 인스턴스를 넘겨받겠지만 메서드 내에서는 정확히 어떤 인스턴스인지 알 길 이 없다. 그래서 instanceof연산자를 이용해서 참조변수가 가르키고 있는 인스턴스의 타입을 체크하고, 적절히 형변환한 다음에 작업을 해야한다.

이로써 조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있기 때문에, 참조변수의 타입과 인스턴스의 타입이 항상 일치하지 않는다는 것을 알 수 있다. 고로 instaceof연산자를 통해 참조변수가 가르키고 있는 인스턴스의 타입을 확인 후 적절히 형변환할 수 있다는 것이다.

public class Test {
    public static void main(String[] args) {
        Car car = new FireEngine();

        if (car instanceof FireEngine) {
            System.out.println("this is a FireEngine instance.");
        }

        if (car instanceof Car) {
            System.out.println("this is a Car instance.");
        }

        if (car instanceof Object) {
            System.out.println("this is a Object instance.");
        }

        System.out.println(car.getClass().getName());
    }
}

===============================================
this is a FireEngine instance.
this is a Car instance.
this is a Object instance.
FireEngine

null은 어떤 것의 instance도 아님.

6. assignment(=) operator

int x = 3;
// 좌측 피연산자 : x
// 우측 피연산자 : 3 

대입연산자는 연산자를 기준으로 우측 피연산자의 값(식이라면 평가값)을 좌측 피연산자에 저장한다

만약 Reference Type인 경우 주소값을 할당하는 것이다.

public class Number {
    private final int value;

    public Number(int value) {
        this.value = value;
    }
}

public class Test {
    public static void main(String[] args) {
        Number numberOne = new Number(1);
        Number numberTwo = new Number(2); // ---(1)
        numberOne = numberTwo; // ---(2)
    }
}
  • 메모리 구조
    • (1) :
    • (2) :

복합 대입 연산자

int i = 3;
i = i + 2;

->

int i = 3;
i += 2;

대입 연산자는 다른 연산자(op)와 결합하여 op=와 같은 방식으로 사용할 수 있다.

  • 종류
    • +=
    • -=
    • *=
    • /=
    • %=
    • &=
    • ^=
    • |=
    • <<=
    • >>=

7. 화살표(->) 연산자

JAVA 8부터 람다 연산자(→)는 람다식을 도입하는 데 사용되는 연산자이다.

람다식은 간단히 말해서 메서드를 하나의 식으로 표현한 것이다. 메서드를 람다식으로 표현하면 메서드의 이름과 반환값이 없어지므로, 람다식을 익명함수이라고도 한다.

람다식을 설명하기 위해 먼저 함수형 인터페이스라는 개념을 알아보자.

함수형 인터페이스

추상메서드가 단 1개인 인터페이스로 하나 이상의 메서드를 가지게 된다면 함수형 인터페이스가 아니다.

  • static메서드와 default메서드의 개수에는 제약이 없다.
  • @FunctionallInterface 애너테이션을 붙혀주면 컴파일러가 함수형 인터페이스를 올바르게 정의하였는 지 확인해주니 꼭 붙이도록 하자!

함수형 인터페이스를 람다식이 나오기 전에는 익명 내부 클래스를 만들어서 사용했다.

@FunctionalInterface
public interface Calculator {
    public int add(int numOne, int numTwo);
}

public static void main(String[] args) {
      Calculator calculator = new Calculator() {
          @Override
          public int add(int numOne, int numTwo) {
              return numOne + numTwo;
          }
      };
}

하지만 람다식을 사용함으로써 간단히 처리할 수 있게 되었다.

람다식 특징

  • Higher - Order Function
    • 함수가 함수를 매개변수로 받을 수 있고 함수를 반환할 수 도 있다.
  • Pure Function
    • 입력받은 값이 동일할 때 결과가 같아야 한다.
  • Anonymous Function

람다식 작성하기

람다식은 익명함수이기 때문에 기존 메서드에서 이름과 반환타입을 제거하고 매개변수 선언부와 몸통사이에 화살표 연산자를 넣어준다.

반환타입 메서드이름 (매개변수 선언) {
	로직 
}

------>

(매개변수 선언) -> {
	로직
}
int max(int a, int b) {
	return a > b ? a : b;
}

------>

(int a, int b) -> {
	return a > b ? a : b;
}

반환값이 있는 메서드의 경우, return문 대신 식으로 대신할 수 있다. 식의 연산결과가 자동적으로 반환값이 되는데 이 때는 문장이 아닌 식이므로 끝에 ;을 붙히지 않는다.

(int a, int b) -> { return a > b ? a : b; }

------>

(int a, int b) -> a > b ? a : b

상황에 따른 람다식 표현법

public interface Calculator {
    public int add(int numOne, int numTwo);
}
  1. 기본 사용법
    : (인수타입 인수명) -> {로직}
    Calculator cal = (int numOne, int numTwo) -> { return numOne + numTwo; };
  1. 인수타입 생략
    : (인수명) → {로직}
    Calculator cal = (numOne, numTwo) -> { return numOne + numTwo; };
  • 람다식에 선언된 매개변수의 타입은 추론이 가능한 경우 생략할 수 있다. (대부분의 경우에 생략가능 하다.)
  1. 인수가 없는 경우
    : () -> {로직}
    public interface Calculator {
        public int add();
    }
    
    Calculator cal = () -> { return "addMethod"; };
  • 람다식에 선언된 매개변수의 타입은 추론이 가능한 경우 생략할 수 있다. (대부분의 경우에 생략가능 하다.)
  1. 중괄호 생략
    : (인수명) -> 로직
    Calculator cal = (numOne, numTwo) -> numOne + numTwo;
  • 실행할 문장이 1개인 경우에는 중괄호 블럭을 생략할 수 있다.
  1. 소괄호, 중괄호 생략
    : 인수명 -> 로직
    public interface Calculator {
        public int square(int num);
    }
    
    Calculator cal = num -> num * num;
  • 매개변수가 하나뿐인 경우 소괄호를 생략할 수 있다.

람다 사용 조건

자바에서 모든 메서드는 클래스내에 포함되어야 한다. 그렇다면 람다식은 어떤 클래스에 포함되어 있을 까?

람다식은 사실 익명 클래스의 객체와 동등하다.

(int a, int b) -> a > b ? a : b
new Object() {
	int max(int a, int b) {
		return a > b ? a : b;
	}
}

그렇다면 이 익명개체의 메서드를 어떻게 호출할 수 있을까?

  • 함수형 인터페이스
    • 추상메서드가 단 1개인 인터페이스를 만들면 이 단 하나의 메서드가 람다식과 1:1로 연결 될 수 있기 때문에 사용이 가능해진다.

java.util.function 패키지에 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의해놓았으니 활용하는 것이 좋다.

8. 3항 연산자

조건식 ?1(true인 경우) :2(false인 경우) 

3항 연산자는 첫 번째 피연산자인 조건식의 평가결과에 따라 다른 결과를 반환한다.

조건식이 true이면 2번째 피연산자인 식1이, false이면 식2가 연산결과가 된다.

int x = 1;
int y = 2;
int result;

// 사용 전 
if (x > y) {
    result = x;
} else {
    result = y;
}

//사용 후 
result = x > y ? x : y;

9. 연산자 우선 순위

출처) https://medium.com/@katekim720/연산자부터-조건-반복문까지-3d5cec6513d4

표에는 나와있지 않지만 괄호의 우선순위가 제일 높으며, 그 다음 산술 > 비교 > 논리 > 대입의 순서이다.

항은 단항 > 이항 > 삼항의 순서이며 연산자들의 진행방향은 좌측에서 우측으로 진행되며 예외적으로 단항 연산자와 대입 연산자의 경우에는 우측에서 좌측으로 진행된다.

10. (optional) Java 13. switch 연산자

기존에 존재하는 switch문에 표현식을 사용할 수 있게 확장되었다.

기존의 방법인 콜론 라벨(:)을 통해 사용하는 것에서 추가적으로 화살표를 라벨()로 사용함으로써 람다식을 사용할 수 있게 되었다. 그리고 fall through가 없기 때문에 break를 사용하지 않아도 된다.

Arrorw labels

  • :대신 를 사용할 수 있다.
static void howMany(int k) {
    switch (k) {
        case 1  -> System.out.println("one");
        case 2  -> System.out.println("two");
        default -> System.out.println("many");
    }
}

howMany(1);
howMany(2);
howMany(3);

=======================
one 
two
many 

switch expressions

swtich가 확장되어 expression으로 사용할 수 있게 되었다.

static void howMany(int k) {
    System.out.println(
        switch (k) {
            case  1 -> "one"
            case  2 -> "two"
            default -> "many"
        }
    );
}
public enum Day { SUNDAY, MONDAY, TUESDAY,
    WEDNESDAY, THURSDAY, FRIDAY, SATURDAY; }

Day day = Day.WEDNESDAY;    
System.out.println(
    switch (day) {
        case MONDAY, FRIDAY, SUNDAY -> 6;
        case TUESDAY                -> 7;
        case THURSDAY, SATURDAY     -> 8;
        case WEDNESDAY              -> 9;
        default -> throw new IllegalStateException("Invalid day: " + day);
    }
);

즉, 다음과 같이 변수 할당을 할 수 있다.

T result = switch (arg) {
    case L1 -> e1;
    case L2 -> e2;
    default -> e3;
};

Yielding a value

yield 키워드가 추가되었다. 새로운 switch 표현식에는 full through가 없고, break를 이용한 값 반환 방법이 없어지고, 그 대안으로 yield를 사용할 수 있게 되었다.

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> {
        System.out.println(6);
        yield 6;
    }
    case TUESDAY -> {
        System.out.println(7);
        yield 7;
    }
    case THURSDAY, SATURDAY -> {
        System.out.println(8);
        yield 8;
    }
    case WEDNESDAY -> {
        System.out.println(9);
        yield 9;
    }
    default -> {
        throw new IllegalStateException("Invalid day: " + day);
    }
};

추가적으로, 콜론 라벨을 사용한 switch 문에서도 사용이 가능하다.

int numLetters = switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        System.out.println(6);
        yield 6;
    case TUESDAY:
        System.out.println(7);
        yield 7;
    case THURSDAY:
    case SATURDAY:
        System.out.println(8);
        yield 8;
    case WEDNESDAY:
        System.out.println(9);
        yield 9;
    default:
        throw new IllegalStateException("Invalid day: " + day);
};

Reference

profile
뒤돌기 전에 기록하자

0개의 댓글

관련 채용 정보