연산자도 마찬가지로 파이썬과의 차이점에 주안점을 두어 살펴본다. 파이썬 말고 다른 언어에는 대부분 있는 증감 연산자가 자바에는 있고, 자료형이 있으므로 연산 시 형변환이 중요하게 여겨진다는 게 큰 차이점이다.
01. 연산자
자바의 연산자는 위 그림에 나와 있는 연산자가 전부다. 대부분 타 언어와 비슷한 부분이 많지만, 세부적으로 들어가면 조금 복잡해질 수 있다. 예를 들어 연산 시 피연산자에 (byte) a > b와 같이 형변환 연산자가 붙어 있는 경우, 결과가 어떻게 처리되는가와 같은 문제에 봉착할 수 있으니 차근차근 알아보자.
1. 산술 > 비교 > 논리 > 대입. 대입은 제일 마지막에 수행된다.
2. 단항 > 이항 > 삼항. 단항 연산자의 우선순위가 이항 연산자보다 높다.
3. 단항 연산자와 대입 연산자를 제외한 모든 연산의 진행방향은 왼쪽에서 오른쪽이다.
1. 산술 변환 (Usual Arithmetic Conversion)
자바는 두 피연산자의 타입이 일치해야 연산이 가능하므로, 피연산자의 타입이 다를 때 연산 전에 형변환 연산자로 타입을 일치시켜야 한다. 예를 들어 int와 float타입을 덧셈하는 경우, 형변환 연산자를 이용해 피연산자의 타입을 둘 다 int 또는 float로 일치시켜야 한다.
public class Main {
public static void main(String[] args) throws IOException {
int i = 100;
float f = 100.5f;
// Case 1
Object result = (Object) (f + i);
System.out.println(result.getClass().getName());
System.out.println(result);
// Case 2
float result2 = f + (float) i;
System.out.println(result2);
// Case 3
int result3 = f + i; // Type mismatch: cannot convert from float to int
System.out.println(result3);
// Case 4
int result4 = (int)f + i;
System.out.println(result4);
}
}
// Case 1
java.lang.Float
200.5
// Case 2
200.5
// Case 3
Exception occered
// Case 4
200
대부분의 경우, 두 피연산자의 타입 중에 더 큰 타입으로 일치시킨다. 작은 타입으로 형변환하면 값의 손실(오버플로우)이 발생할 수 있기 때문이다. 예를 들어 다음과 같이 int형 변수 i와 float형 변수 f를 연산하는 경우, 형변환 연산자를 생략하면 다음과 같이 자동적으로 더 큰 타입인 float타입으로 형변환되어 연산된다(Case 1). 이렇게 연산전에 타입 일치를 위해 자동 형변환되는 것을 산술 변환 또는 일반 산술 변환이라고 한다.
Case 1의 상황을 더 자세히 기술하면 다음과 같다(Case 2). 반대로 형변환 연산자 없이 더 작은 타입으로 변환하려고 하는 경우 다음과 같이 타입 불일치 예외가 발생한다(Case 3). 마지막으로 float타입을 int형으로 변환해 연산을 하게 되면 소수점 이하를 떼버리므로 값의 손실이 발생한다(Case 4).
산술 변환의 규칙
1. 두 피연산자의 타입을 같게 일치시킨다. (더 큰 타입으로 일치)
2. 피연산자의 타입이 int보다 작은 타입이면 int로 변환된다.
❗ 모든 연산에서 산술 변환이 일어나지만, 쉬프트 연산자(<<, >>), 증감 연산자(++, --)는 예외다.
2. 사칙 연산자 ( + - * / )
사칙 연산은 일반적으로 수학에서 배운 것과 같다. 곱셈이나 나눗셈이 덧셈이나 뺄셈보다 우선하고, 어떤 수를 0으로 나누면 에러가 발생한다.
public class Main {
public static void main(String[] args) throws IOException {
// Case 1
int a = 10;
int b = 4;
System.out.println(a / b);
float t = a / b;
System.out.println(t);
// Case 2
System.out.println((float) a / b);
// Case 3
byte c = 10;
byte d = 20;
byte e = c + d; // Type mismatch: cannot convert from int to byte
// Case 4
byte f = (byte) (c + d);
System.out.println(f);
// Case 5
byte g = (byte) (c * d);
System.out.println(g);
}
}
// Case 1
2
2.0
// Case 2
2.5
// Case 3
Exception occered
// Case 4
30
// Case 5
-56
다음과 같은 경우들을 보자. 우선 int형 변수 a, b끼리의 나눗셈 연산에서 연산 결과는 2.5가 아닌 2이다. int형은 소수점을 저장하지 못하므로 정수만 남고 소수점 이하는 버려지기 때문이다. 아래 float형 변수 t에 같은 연산 결과를 집어넣어도 마찬가지로 2.0이 출력된다. 오른쪽에서 이미 int형끼리 연산이 끝난 뒤에 형변환해서 변수 t에 저장되기 때문이다(Case 1). 결국 우리가 원하는 값을 얻기 위해선 a, b 둘 중 하나를 실수 타입으로 변환한 뒤 연산해야 한다(Case 2).
byte형끼리의 연산을 보자. byte형은 int형보다 작기 때문에 앞선 절의 산술 변환 규칙에 따라 int형으로 변환된 뒤 연산된다. 하지만 int형(4byte)으로 연산된 결과를 byte(1byte)형 변수 e에 넣으려 하므로 에러가 발생한다(Case 3). 크기가 작은 자료형의 변수를 큰 자료형의 변수에 저장할 때는 자동으로 형변환되지만, 반대로 큰 자료형의 값을 작은 자료형의 변수에 저장하려면 명시적으로 형변환 연산자를 이용해 변환해주어야 한다(Case 4).
마지막으로 byte형 변수 c, d를 곱한 결과를 다시 byte형 변수에 저장했더니 -56이 나왔다. 연산 시 c, d는 byte형이므로 int형으로 변환된 뒤 200으로 연산되고 다시 형변환 연산자 (byte)로 byte형으로 형변환할 때 문제가 발생했다. 4byte 크기의 값을 1byte에 넣으려 하므로 4byte 크기의 값에서 하위 8bit(1byte)만을 저장하는 손실이 발생했기 때문이다. 이처럼 값 손실을 예방하기 위해서는 충분히 큰 자료형을 사용해야 한다(Case 5).
'프로그래밍 > Java' 카테고리의 다른 글
[Java] Java Build Tools 발전과정 - (3) Gradle (0) | 2023.10.27 |
---|---|
[Java] Java Build Tools 발전과정 - (2) Maven (0) | 2023.10.24 |
[Java] Java Build Tools 발전과정 - (1) Make vs Ant (0) | 2023.10.20 |
[Java] Java기초 - (2) 기초 문법 - 변수와 리터럴 (0) | 2023.10.12 |
[Java] Java기초 - (1) 역사 (0) | 2023.10.11 |