Search
Duplicate

[3주차] 연산자

목차

목표

자바가 제공하는 다양한 연산자 학습

학습할 것

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

1.산술 연산자

산술(arithmetic) 연산자는 수학적인 계산에 사용되는 연산자다.
산술 연산자는 산술(arithmetic)이라는 의미 그대로 수학적인 계산에 사용되는 연산자로 유치원 및 초등학생 때 배우는 덧셈, 뻴셈, 곱셈, 나눗셈 등의 수학적 기호 연산자 를 뜻한다. 다만, 수학에서 사용하는 연산자와 프로그래밍에서 사용하는 연산자는 의미가 조금 다르니 그에 대해 살펴본다.

사칙 연산자 ( + - * /)

덧셈(+), 뺄셈(-), 곱셈(*), 나눗셈(/)은 정말 자주 쓰이는 연산자로 모두가 잘 아는 사칙연산이다.
우선순위 역시 동일하게 적용되어 곱셈과 나눗셈은 덧셈이나 뺄셈보다 높기에 우선 적용된다.
사용 예제코드
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) }
Java
복사
실행 결과
(1): 10 / 4이면 2.5가 맞지만 출력 결과를 보면 2가 나온다 왜그럴까? 그 이유는 자료형에 있습니다. 연산에 사용된 두 피연산자는 모두 int 형이다.. 그럼 연산결과 역시 int형으로 반환을 하는데 결과값인 2.5는 실수형이기에 소숫점을 버리고 정수형인 2로 반환을 합니다. 그리고 이 때, 소숫점은 버림을 하지 반올림을 하지 않는다. 그럼 어떻게 소숫점까지 표현한 정확한 결과를 얻을 수 있을까? 그럼 피연산자중 한 쪽을 실수형으로 변환해야 한다. 위 예제코드 중 (2) 항목을 보면 b를 실수형(float)으로 변경해주었다. 위의 연산과정을 보면 두 피연산자의 타입이 각각 int형과 float형으로 일치하지 않기에 타입을 맞춰줘야하는데, 보통 범위가 넓은 쪽으로 매치가 되기에 int보다 범위가 넓은 float타입으로 일치시킨 뒤 연산을 수행한다.

피연산자가 정수형인 경우 나누는 수로 0을 사용하면 에러가 발생한다.

피연산자가 정수형인 경우, 나누는 수로 0을 사용하게 될 경우 ArithmeticException이 발생한다.
0이 아닌 부동 소숫점값(0.0f, 0.0d)를 사용하는것은 가능하지만 이 경우엔 무한대(Infinity)가 반환된다.
피연산자가 유한수가 아닌 경우의 연산결과

작은 범위로의 형변환도 가능하다.

int a = 10;; float b = 4.0f; int value = a / b; byte c = 10; byte d = 30; byte value2 = c * d;
Java
복사
int형 피연산자와 float형 피연산자간에 산술연산을 하면 범위가 더 큰 쪽으로 자동 캐스팅되어 연산을 수행한다. 그럼 int형으로 결과를 받고싶을 경우엔 어떻게 해야할까? 명시적 형변환을 해주면된다.
int a = 10;; float b = 4.0f; int value = (int)(a / b); System.out.println(value); byte c = 10; byte d = 30; byte value2 = (byte)(c * d); System.out.println(value2);
Java
복사
[실행 결과]
2 44
대신, 더 좁은 범위로 형변환을 하는 것이기 때문에 값의 손실이 발생할 수 있다.
⇒ int형을 byte형으로 변환하는경우 하위 8자리(1byte)만 보존하기에 그외에 값이 손실된다.

연산전에 충분히 큰 자료형을 사용해서 오버플로우를 막아라.

public static void main(String[] args) { int a = 1_000_000; int b = 2_000_000; long value = a * b; System.out.println(value); }
Java
복사
⇒ value로 무엇이 출력될까? 2 x 10¹² 값을 long(8 byte)에 저장하니 정상적으로 출력이 될 것 같지만, 실제로 출력 되는 값은-1454759936 로 엉뚱한 값이 출력된다. 그 이유는 long형으로 자동형변환을 하는 시점에서는 이미 오버플로우가 발생하여 값이 변조되었기 때문에 형변환이 된다한들 값이 변하지 않는 것이다.
int형 연산으로 overflow 발생
그렇기에 해당 연산이 진행되기 전 하나의 피연산자를 충분한 크기의 자료형으로 형변환을 해서 타입일치를 시켜 충분한 범위를 확보해야 한다.
public static void main(String[] args) { int a = 1_000_000; int b = 2_000_000; long value = (long)a * b; System.out.println(value);//2000000000000 }
Java
복사
1.
along으로 형변환되며 충분한 범위 확보
2.
ab의 타입이 일치하지 않기때문에 더 넓은 범위인 long으로 b를 형변환하여 타입 일치
3.
연산 수행하여 value에 대입

문자도 산술연산이 가능하다.

산술연잔자를 숫자형의 자료형 뿐 아니라 문자도 가능하다. 가능한 이유는 문자도 실제로 해당 문자의 유니코드(부호없는 정수)로 바꾸어 저장되기 때문이다.
System.out.println('c' - 'b'); // 1
Java
복사
⇒ c의 유니코드는 99이고, b의 코드는 98이다. 그렇기에 'c' - 'b'는 99 - 98이 되며 결과값은 1이된다.
이를 응용하면 특정 문자의 다음 문자값을 가져오는것도 가능하다.
char c = 'c'; System.out.println((char)(c + 1)); //d
Java
복사
c의 코드는 99이며 여기에 1을 더한 값인 100을 다시 char로 형변환 해준다면 d가 출력된다.

리터럴간의 연산

char value = 'a' + 1;
Java
복사
⇒ 위 코드는 에러가 발생하지 않는다. char형과 int형간의 연산이면 더 큰 범위인 int형으로 형변환되기에 에러가 발생해야 하지 않을까? 이는 위 코드의 연산이 리터럴간의 연산이기 때문인데, 상수 또는 리터럴 간의 연산은 실행 과정동안에 변하는 값이 아니라 컴파일 시에 계산해서 그 결과로 대체하는 것이기 때문에 컴파일 후에는 이미 char value = 'b'; 가 되어있기 때문에 덧셈 연산이 수행되지 않는다.
하지만, 수식에 변수를 통한 연산은 컴파일러가 미리 계산을 할 수 없기 때문에 명시적인 형변환을 해줘야 에러가 발생하지 않는다.

나머지 연산자 %

나머지 연산자는 좌측의 피연산자를 우측 피연산자로 나누고 남은 나머지 값을 반환하는 연산자다.
결국 나눗셈을 수행하기에 나눗셈과 같이 나누는 수(우측 피연산자)로 0을 사용할 수 없다.
int remainderValue = 10 % 3; System.out.println(remainderValue);// 1
Java
복사

2.비트 연산자 (& | ^ ~ << >>)

비트 단위로 논리 연산을 할 때 사용하는 연산자
비트 연산자는 피 연산자를 비트단위로 연산하는데, 피 연산자를 이진수로 표현했을 때의 각 자리를 규칙에 따라 연산을 수행하며, 피연산자로 실수는 허용하지 않으며 정수만 허용된다.
비트 연산자의 연산 결과
1.
| (OR연산자): 피연산자 중 한 쪽의 값이 1이면, 1을 결과로 얻는다. 그 외에는 0을 얻는다.
주로 특정 비트의 값을 변경할 때 사용한다.
2.
& (AND연산자): 피연산자 양 쪽이 모두 1일때만 1을 결과로 얻는다. 그외에는 0을 얻는다.
주로 특정 비트의 값을 추출할 때 사용한다.
3.
^ (XOR연산자): 피연산자의 값이 서로 다를때만 1을 결과로 얻는다. 그외에는 0을 얻는다.
같은 값으로 두고 XOR 연산을 수행하면 원래값으로 돌아오기에 간단한 암호화에 사용한다.

비트 전환 연산자 ~

논리부정 연산자(!) 와 유사한 연산자로 피연산자를 2진수로 표현했을 때 0은 1로 1은 2로 바꾼다.
물론 해당 연산자도 피연산자가 정수형에만 사용하며 실수는 사용할 수 없다.
이러한 비트 전환연산자는 음수를 표현하기위해 사용되는데 음수를 표현할 수 없는 컴퓨터의 제한적인 상황을 1의 보수를 통해 해결한 것이다. 아래 과정을 통해 10진수의 10을 어떻게 음수로 표현하는지 비트 전환 연산자를 통해 살펴보자.
1.
2진수 10의 1의 보수
⇒ 가장 왼쪽 비트는 부호비트(MSB)로 부호를 결정하는 비트이다.
2.
여기서 10진수 10을 비트전환 연산한 결과는 -11이고, 이 값은 10의 '1의 보수'이다.
3.
1의 보수에 1을 더하면 음수가되기에 -11에 1을 더하면 -10이되어 -11이 10의 '1의 보수'가 맞다는것이 확인 가능하다.

3.관계 연산자

두 피연산자를 비교하는데 사용되는 연산자
주로 조건문과 반복문의 조건식에 사용되며, 연산결과는 오직 truefalse 둘 중의 하나이다.
관계연산자 역시 이항 연산자이기에 비교하는 피연산자의 타입이 다르면 자료형의 범위가 큰 쪽으로 타입을 일치시킨 뒤 비교한다.

1. 대소비교 연산자 < > <= >=

두 피 연산자간의 값의 크기를 비교하는 연산자로 참일경우 true, 아닐 경우 false를 반환한다.
기본형은 boolean을 제외하고 다 사용가능하지만 참조형에는 사용할 수 없다.
> : 좌변 값이 크면 true 아니면 false
System.out.println(2 > 1); // true System.out.println(2 > 2); // false
Java
복사
<: 좌변 값이 작으면 true, 아니면 false
System.out.println(1 < 2); // true System.out.println(2 < 2); // false
Java
복사
>=: 좌변 값이 크거나 같으면 true 아니면 false
System.out.println(2 >= 2); // true System.out.println(2 >= 1); // true System.out.println(1 >= 2); // false
Java
복사
>=: 좌변 값이 작거나 같으면 true 아니면 false
System.out.println(1 <= 2); // true System.out.println(2 <= 2); // true System.out.println(3 <= 2); // false
Java
복사

2. 등가비교 연산자 == !=

두 피연산자의 값이 같은지 또는 다른지를 비교하는 연산자이다. 대소비교 연산자와는 다르게 참조형을 포함하여 모든 자료형에서 사용이 가능하다. 참조형의 경우에는 객체의 주소값을 저장하고 있기에 해당 주소값을 비교하여 값을 비교할 수 있다.
기본형과 참조형은 서로 형변환이 가능하지 않기 때문에 등가비교 연산자로 기본형과 참조형을 비교할 수는 없다.
== : 두 값이 같으면 true, 아니면 false
class Person { ... } Person person = new Person(); Person personSecond = person; System.out.println(2 == 2); // true System.out.println("abc" == "abc"); // true System.out.println(2 == "2"); // false System.out.println(person == personSecond); // true
Java
복사
!= : 두 값이 다르면 true, 아니면 false
class Person { ... } Person person = new Person(); Person personSecond = person; System.out.println(2 != 1); // true System.out.println("abc" != "abc"); // false System.out.println(2 != '2'); // true System.out.println(person != personSecond); // false
Java
복사
그리고, 관계연산자 역시 이항 연산자이기에 연산 수행 전 형변환을 통해 두 피연산자의 값을 일치시킨 뒤 비교하는데, 예를들어 10 == 10.0f는 더 범위가 넓은 자료형인 float으로 10을 변환한 뒤 비교한다. 하지만, 주의해야할 사항이 있다. 아래 코드를 보자
float f = 0.1f; double d = 0.1; System.out.println(f == d); // false
Java
복사
⇒ 분명 위에서 예를 들 때 10 == 10.0f10int 형으로 변경되며 true가 반환되었는데 어째서 0.1 == 0.1ffalse가 되는 것일까? 그 이유는 실수형은 근사값으로 저장되기에 오차가 발생할 수 있기 때문이다. 0.1f는 저장할 때 2진수로 변환하는 과정에서 오차가 발생한다. 물론 double도 오차가 발생하지만 float 타입의 0.1f보다는 적은 오차로 저장된다.
그렇다면 어떻게 실수형을 비교해야할까?
1.
더 좁은 범위의 실수형(float)으로 형변환하여 비교한다.
2.
어느정도의 오차를 무시하고 두 타입을 앞에서 몇자리만 절삭하여 비교한다.

4.논리 연산자 && || !

둘 이상의 조건을 결합하여 하나의 식으로 만들어주는 연산자
지금까지 여러 연산자들을 살펴보았다. 논리연산자는 이런 연산자들을 사용한 각각의 조건들을 이어줄 수 있는 역할을 한다.
학교의 학점을 예로들어 생각해보면 소프트웨어 공학 과목에서 B학점은 80점 이상 90점 미만이라고 하면 조건은 하나로 불가능하다. 점수 >= 80 이면서 점수 < 90 두 조건을 모두 만족해야 한다. 이를 논리연산자를 통해 하나의 식으로 결합할 수 있다.
||(OR 결합): 피연산자 중 어느 한 쪽만 true이면 true를 결과로 얻는다.
//1. 길드가입조건: 레벨이 60이상이거나 보유 골드가 10만골드 이상인 경우 if(level >= 60 || gold >= 100_000){ ... }
Java
복사
&&(AND결합): 피연산자 양쪽 모두 true이어야 true를 결과로 얻는다.
//1.청년 내일채움 공제 가입조건중 나이는 만15세 이상 35세 미만이다. if(age >= 15 && age < 35){ ... }
Java
복사

효율적인 연산(short circuit evaluation)

논리 연산자의 또 다른 특징은 효율적인 연산을 한다는 것인데, OR연산인 || 의 경우, 두 피연산자 중 어느 한쪽만 참이여도 참이기에 좌측에 우선 평가 될 피연산자에 참이 될 가능성이 높은 피연산자를 위치하면 좌측의 피연산자가 참일 경우 우측의 피연산자 수행을 하지않고 진행하게 된다.
AND연산인&& 도 마찮가지로 어느 한 쪽만 거짓이여도 결과는 거짓이 되기 때문에 false를 반환할 확률이 높은 평가식을 좌측 피연산자위치에 놓을 경우 더 빠른 연산결과를 얻을 수 있다.

논리부정 연산자 !

이 연산자는 피연산자의 결과를 반대로 반환하는데, true이면 false를 반환하고 falsetrue를 반환한다. 마치 스위치를 껐다켰다 하듯이 논리 부정연산자를 중첩하면 true와 false가 계속 뒤바뀐다. 이 논리부정 연산자가 자주 쓰이는 곳은 조건문과 반복문의 조건식인데, 이 연산자를 이용하여 반복을 제어하거나 실패에대한 조건을 적용할 수 있다.
//1. 논리형 변수 isLoop가 false가 될 때까지 반복하라. boolean isLoop = true; int count = 0; while(isLoop) { if(count >= 10) isLoop = false; count++; System.out.println("count: "+ count); } //2. 참가자의 이름이 catsbi가 아닌사람만 입장시켜라. if(!(nickName == "catsbi")){ enter(); }
Java
복사

5.instanceof

참조변수의 실제 타입을 알아보기 위해 사용한다.
참조변수 instanceof 타입(클래스)
반환 타입은 Boolean이며 반환 값이 true일 경우 참조 변수가 검사한 타입으로 형변환이 가능하다는 의미가 된다.
이는 조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있기 때문에, 참조변수의 타입과 인스턴스 타입이 항상 같지는 않다는 점을 기억한다면, instanceof 연산자를 이용해 참조변수 c가 가리키고 있는 인스턴스의 타입을 확인 후 적절히 형변환할 수 있다.
예를들어 Parent라는 클래스와 이를 상속하는 Child가 있다면 Child는 Parent로 형변환이 가능하다.
class Parent { ... } class Child extends Parent { ... } Parent parent = new Child(); System.out.println(parent instanceof Child);//true System.out.println(parent instanceof Parent);//true System.out.println(parent instanceof Object);//true
Java
복사

6.assignment(=) operator

변수와 같은 저장공간에 값 또는 수식의 연산결과를 저장하는데 사용된다.
저장될 피연산자 = 저장할 피연산자
대입연산자는 연산자를 기준으로 우측 피연산자의 평가결과를 좌측 피연산자에 대입한다.
그리고, 연산자들 중 가장 낮은 우선순위를 가지고 있기에 항상 제일 늦게 수행된다. 그리고 진행방향은 우측에서 좌측으로 진행되기에 x = 3이면 3을 x에 대입하고, x = y = z = 3이면 z부터 차례대로 대입된다.

lvalue와 rvalue

대입 연산자의 왼쪽 피연산자를 lvalue(left value) 오른쪽 피연산자를 rvalue(right value)라 한다.rvalue는 변수 뿐 아니라 식이나 상수 등 모두 가능하지만, lvalue는 반드시 변수처럼 값을 변경할 수 있는 것이어야 한다.
int i = 0; 3 = i + 3; //에러. lvalue가 저장할 수 있는 공간이 아닌 리터럴이다. i + 3 = i; //에러. lvalue의 평가결과는 i + 3 -> 0 + 3 -> 3이기에 리터럴으로 저장할수 없다.
Java
복사

복합 대입 연산자(op=)

대입 연산자는 다른 연산자(op)와 결합하여 op=형태로 사용될 수 있다.
예를 들어, 7살인 철수의 나이가 생일이지나 8살이 되는것을 코드로 표현하면
//before case int chulsuAge = 7; chulsuAge = chulsuAge + 1; //after case int chulsuAge = 7; chulsuAge += 1;
Java
복사
op= 연산자를 안쓰면 before case처럼 작성을 해줘야 한다. 하지만, op=연산자를 쓰면 after case처럼 간결하게 쓸 수 있다. 그리고 결합된 두 연산자는 반드시 공백없이 붙혀써야 한다.
그리고 주의해야 할 점은 복합연산자의 우측에 추가적인 연산자가 있는경우 이를 먼저 수행해줘야 한다.
복합 대입 연산자의 종류
표에서 왼쪽은 복합 연산자의 사용 예이고 우측은 대입 연산자를 이용한 왼쪽과 동일한 의미의 식이다. 여기서 마지막을 보면 i = 10 + j인데 우측과 같이 10 + j를 먼저 수행해야지 i*10 + j으로 계산해서는 안된다.

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

메서드를 하나의 식(expression)으로 표현한 것.
기존의 메서드를 람다식(화살표 연산자)으로 표현하면 메서드의 이름과 반환값이 없어져서 익명함수(anonymous function)이라고도 부르는데, 메서드를 하나의 인수로 취급할 수도 있어서, 유연한 프로그래밍을 가능하게 해준다. 이 람다식은 Java 8 이후 나온 기술인데, 이 기술을 얘기하려면 함수형 인터페이스에 대해 알아야한다. 간략하에 함수형 인터페이스에 대해서 설명한다.

함수형 인터페이스(Functional Interface)

추상메서드가 단 1개인 인터페이스로 하나 이상의 메소드를 가지면 함수형 인터페이스가 아니다..
staticdefault method는 추가적으로 구현이 가능하다.
함수형 인터페이스로 사용할 인터페이스에는 @FunctionInterface 애너테이션을 붙혀주면 컴파일러에서 함수형 인터페이스를 확인해준다.
이런 함수형 인터페이스를 람다식이 나오기 전에는 익명 내부 클래스를 만들어 사용했습니다.
@FunctionalInterface public interface Calculator { public int cal (int numA, int numB); } public static void main(String[] args){ Calculator calculator = new Calculator() { @Override public int cal(int numA, int numB) { return numA + numB; } }; }
Java
복사
기존의 방법(익명 내부 클래스)은 코드가 길어져 가독성이 떨어집니다. 하지만 이를 람다식을 이용해 간단히 사용할 수 있습니다.

람다식 특징

함수를 일급 객체로 사용할 수 있다. 함수형 인터페이스를 인라인 형태로 구현한 오브젝트를 메서드의 인수, 반환타입, 변수로 만들어 사용이 가능하다.
고차 함수(Higher - Order Function)
함수가 함수를 매개변수로 받을 수 있고 함수를 반환할 수도 있다.
순수 함수(Pure Function)
입력받은 값이 동일할 때 결과가 같아야 한다.

람다식 작성하기

람다식은 익명함수(anonymous function)이기에 기존 메서드에서 이름과 반환타입을 제거하고 매개변수 선언부와 몸통사이에 화살표 연산자를 넣어준다.
//before case int 메소드명(int a, int b) { 로직 }
Java
복사
//after case - Lambda (int a, int b) -> { 로직 }
Java
복사
여기서 로직에 반환값이 있다면 return 문 대신 식(expression)으로 대신할 수 있다. 식의 연산결과가 자동으로 반환값이 되는데 이 때는 문장이 아닌 식이기에 세미콜론(;)을 붙히지 않는다.
그리고 람다식 선언부의 인수타입도 타입추론이 가능한경우 생략할 수 있다.
//덧셈 람다식 (a, b) -> a + b
Java
복사

상황별 람다식 표현법

람다식은 상황에따라 다양한 표현법으로 사용할 수 있으며, 상황에따라 블럭을 생략하기도하고, 선언부의 괄호를 생략하기도 합니다.
예제에 사용될 인터페이스
public interface Calculator { public int cal (int numA, int numB); }
Java
복사
1.
기본 사용법 : (인수타입 인수명) → {로직};
Calculator cal = (int numA, int numB) -> { return numA + numB; };
Java
복사
2.
인수타입 생략
:(인수명) → {로직}
Calculator cal = (numA, numB) -> { return numA + numB; };
Java
복사
3.
인수가 없는 경우
: ()→{로직}
public interface Calculator { public int cal (); } Calculator cal = () -> { return "NOT_EXISTS_PARAMETER"};
Java
복사
인수가 없는 경우 빈 괄호를사용합니다.
4.
중괄호 생략 :(인수명) → 로직;
Calculator cal = (a, b) -> a + b;
Java
복사
실행할 문장이 1개인 경우에는 중괄호 블럭을 생략할 수 있습니다.
5.
소괄호 중괄호 생략
:인수명 → 로직;
public interface Calculator { public int cal (int num); } Calculator cal = num -> num * num;
Java
복사
매개변수와 평가식이 모두 1개인 경우 괄호와 중괄호를 모두 생략할 수 있습니다.

람다 사용 조건

자바에서 모든 메서드는 클래스내에 포함되어야 하는데, 람다식은 어떤 클래스에 포함되어 있는 것일까? 람다식은 사실 익명 클래스의 객체와 동등하다.
그래서 이 익명객체의 메서드를 어떻게 호출할 수 있는가?
함수형 인터페이스(functional interface)(Single Abstract Method)
: 추상메서드가 단 1개인 인터페이스를 만들면 이 단 하나의 메서드가 람다식과 1:1로 연결될 수 있기 때문에 사용이가능해진다.
staticdefault method는 추가적으로 구현이 가능하다.
함수형 인터페이스로 사용할 인터페이스에는 @FunctionInterface 애너테이션을 붙혀주면 컴파일러에서 함수형 인터페이스를 확인해준다.
Java에서 기본으로 제공하는 함수형 인터페이스가 있으며 이를 활용할 수도 있다.

8.3항 연산자

조건연산자의 결과에 따라 평가식을 반환하는 연산자
3항 연산자는 첫 번째 피연산자인 조건식의 평가결과에 따라 다른 결과를 반환한다.
조건식이 true이면 2번째 피연산자인 식1이, 조건식이 false이면 3번 째 피연산자인 식2이 반환된다. 조건식의 가독성을 위해 괄호로 감싸도 되지만 필수는 아니다.
삼항연산자의 식1과 식2에는 주로 값이 오지만 경우에 따라 또다른 연산식(ex: 삼항연산자)이 올 수도 있다.
기본 사용법
int a = 1; int b = 2; String result = ""; //before case if(a < b) { result = "a가 작습니다"; } else { result = "b가 작습니다"; } //after case result = a < b ? "a가 작습니다" : "b가 작습니다"
Java
복사
2번째 3번째 피연산자에 또다른 연산식 사용
int a = 1; int b = 2; int c = 3 //after case String result = a < b ? c < a ? "c가 제일 작습니다" : "a가 제일 작습니다" : "b가 작습니다"
Java
복사

9.연산자 우선 순위

연산자가 둘 이상인 경우 우선적으로 연산되어야 하는 순위
식에 사용된 연산자가 둘 이상인 경우, 연산자의 우선순위에 의해서 연산순서가 결정된다.
대부분, 이미 우리가 아는 순위인경우가 많은데, 예를 들면 곱셈과 나눗셈은 덧셈 뺄셈보다 우선순위가 높기에 평가식에 덧셈과 곱셈이 같이 있는경우 곱셈부터 계산을 해야 한다.

연산자 우선순위 표

괄호의 우선순위가 제일 높으며, 그 다음 산술 > 비교 > 논리 > 대입의 순서입니다.
항은 단항 > 이항 > 삼항의 순서입니다. 연산자들의 진행방향은 좌측에서 우측으로 진행되며 단항 연산자와 대입 연산자의 경우에는 우측에서 좌측으로 진행됩니다.

10.Java 13. switch operator

Preview 이기 때문에 --enable-preview 옵션을 줘야 사용가능한 기능.
기존의 swtich 문에 표현식을 사용할 수 있게 확장되었는데, 기존의 방법인 콜론 라벨(:) 을 통해
사용하는 것에서 추가적으로 화살표를 라벨()로 사용할 수 있게되면서 람다식(Lambda)을 사용할 수 있게 되었습니다. 그리고 full through가 없기 때문에 break를 쓰지 않아도 됩니다.

Arrow label

static void howMany(int k) { switch (k) { case 1 -> System.out.println("one"); case 2 -> System.out.println("two"); default -> System.out.println("many"); } }
Java
복사
위 메소드를 아래와 같이 사용할 수 있다.
howMany(1); howMany(2); howMany(3);
Java
복사
[실행 결과]
one two many

switch expressions

switch가 확장되어 expression으로 사용할 수 있게 되었다. 예를들어 위의 howMany 메소드가 switch expression으로 사용하기위해 작성해본다.
static void howMany(int k) { System.out.println( switch (k) { case 1 -> "one" case 2 -> "two" default -> "many" } ); }
Java
복사
위 코드는 일반적으로 아래와 같이 변수할당을 해 사용할 수 있다는 말이다.
T result = switch (arg) { case L1 -> e1; case L2 -> e2; default -> e3; };
Java
복사

Yielding a value

yield 키워드가 추가되었다. 새로운 switch 표현식에서는 full through 가 없고, break를 이용한 값 반환 방법이 없어지고, 그 대안으로 yield를 사용할수 있게 되었다.
int j = switch (day) { case MONDAY -> 0; case TUESDAY -> 1; default -> { int k = day.toString().length(); int result = f(k); yield result; } };
Java
복사
더하여 : 을 이용한 기존의 switch문에서도 사용이 가능하다.
int result = switch (s) { case "Foo": yield 1; case "Bar": yield 2; default: System.out.println("Neither Foo nor Bar, hmmm..."); yield 0; };
Java
복사

참고

자바의 정석 (남궁성)