티스토리 뷰

목표

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

 

학습할 것

  • 산술 연산자

  • 비트 연산자

  • 관계 연산자

  • 논리 연산자

  • instanceof

  • assignment(=) operator

  • 화살표(->) 연산자

  • 3항 연산자

  • 연산자 우선 순위

  • Java 13. switch 연산자

 

1. 산술 연산자

일반적으로 말하는 사칙연산인 더하기(+), 빼기(-), 곱하기(*), 나누기(/)와 나머지를 구하는 연산자(%)를 포함한다.산술 연산자는 boolean 타입을 제외한 모든 기본 타입에 사용할 수 있다.

 

산술 연사자의 특징은 피연산자들의 타입이 동일하지 않을 경우 피연산자들의 타입을 일치시킨 후 연산을 수행한다.

 

① 피연산자들이 모두 정수 타입이고, int 타입(4byte)보다 크기가 작은 타입일 경우 모두 int 타입으로 변환 후, 연산을 수행한다.

     ex) byte + byte → int + int = int

② 피연산들이 모두 정수 타입이고, long 타입이 있을 경우 모두 long 타입으로 변환한 후, 연산을 수행한다.

     ex) int + long → long + long = long

③ 피연산자 중 실수 타입(float 타입, double 타입)이 있을 경우, 크기가 큰 실수 타입으로 변환 후, 연산을 수행한다.

     ex) int + double → double + double = double

 

char 타입도 정수 타입이므로 산술 연산이 가능하며, 산술 연산 시 char 타입은 int 타입으로 변환되고, 산출 타입은 int 타입이다.

 

public class CharOperationExample {
	public static void main(String[] args) {
    	char c1 = 'A' + 1; // Output : B
        char c2 = 'A'; // Output : A
        // char c3 = c2 + 1; //컴파일 에러
    }
}

위의 코드를 살펴보면, c1의 경우 'A' + 1은 리터럴 문자에 1을 더한 것으로 문자 A는 유니코드로 65라는 값을 가지므로 c1의 값은 66이 된다. 따라서, 66인 유니코드는 문자 B이므로 c1에는 문자 B가 저장된다.

(자바는 리터럴 간의 연산은 타입 변환 없이 해당 타입으로 계산한다.)

 

변수 c3의 경우 컴파일 에러가 발생하게 되는데 변수 c2에 1을 더하면 c2가 int 타입으로 변환되고 1과 연산되어

산출 타입은 int 타입이 되기 때문이다. 따라서 char 변수 c3에 대입을 할 수 없어 컴파일 에러가 발생한다.

 

2. 비트 연산자

비스 연산자는 데이터를 비트(bit) 단위로 연산을 수행하며 0과 1이 피연산자가 된다.

0과 1로 표현이 가능한 정수 타입만 비트 연산을 수행할 수 있기 때문에 실수 타입인 float, double은 비트 연산을 할 수 없다.

 

비트 연산자의 경우 ①비트 논리 연산자 ②비트 이동 연산자로 나누어 표현할 수 있다.

 

① 비트 논리 연산자

비트 논리 연산자에는 &(AND, 논리곱 / 두 비트 모두 1일 경우에만 연산 결과가 1), |(OR, 논리합 / 두 비트 중 하나만 1이면 연산 결과는 1), ^(XOR, 배타적 논리합 / 두 비트 중 하나는 1이고 다른 하나가 0일 경우 연산 결과는 1), ~(NOT, 논리 부정 / 보수)가 있다.

 

비트 연산자는 피연산자들을 int 타입으로 자동 타입 변환한 후 연산을 수행한다. 그렇기 때문에 byte, short, char 타입을 비트 논리 연산하게 되면 결과값은 int 타입이 되기 때문에 int 타입이 아닌 다른 타입에 연산 결과를 저장하게 될 경우 컴파일 에러가 발생한다.

 

② 비트 이동 연산자

비트 이동 연산자는 a<<b(정수 a의 각 비트를 b만큼 왼쪽으로 이동시키고 빈자리는 0으로 채움), a>>b(정수 a의 각 비트를 b만큼 오른쪽으로 이동시키고 빈자리는 정수 a의 최상위 부호 비트와 같은 값으로 채움), a>>>b(정수 a의 각 비트를 b만큼 오른쪽으로 이동시키고 빈자리는 0으로 채움) 3가지로 나뉘며, 정수 데이터의 비트를 좌측 또는 우측으로 이동시키는 연산을 수행한다.  

 

3. 관계 연산자

관계 연산자는 대소 또는 동등을 비교해서 boolean 타입인 true/false를 산출하게 된다.

대소 연산자는 boolean 타입을 제외한 기본 타입에 사용할 수 있고, 동등 연산자는 모든 타입에 사용될 수 있다.

 

만약 피연산자가 char 타입인 경우 유니코드 값으로 관계 연산을 수행하게 된다.

관계 연산자에서도 연산을 수행하기 전에 타입 변환을 통해 피연산자의 타입을 일치시킨다.

타입을 일치시킬 때는 크기가 작은 타입이 큰 타입으로 변환된다.

 

한가지 예외 사항은 0.1 == 0.1f 의 경우 0.1f가 좌측 피연산자의 타입인 double로 변환되어  0.1 == 0.1이 되고 결과값으로 true가

나와야 하지만 이 결과값은 false가 산출되는데 이유는 이진 포맷의 기수를 사용하는 모든 부동소수점 타입은 0.1을 정확히 표현할 수 없어 0.1f는 0.1의 근사값으로 표현되어 0.1보다 약간 큰 값이 되어버린다. 이럴 경우 해결책은 피연산자를 모두 float 타입으로 강제 타입 변환한 후 관계 연산을 수행하거나 정수로 변환해서 비교해야 한다.

 

4. 논리 연산자

논리 연산자는 &&(AND, 논리곱 / 피연산자 모두가 true일 경우에만 연산 결과는 true), ||(OR, 논리합 / 피연산자 중 하나만 true이면 연산 결과는 true), ^(XOR, 배타적논리합 / 피연산자가 하나는 true이고 다른 하나가 false일 경우에만 연산 결과는 true),  !(NOT, 논리부정 / 피연산자의 논리값을 바꿈) 4가지로 나눌 수 있으며 논리 연산자의 피연산자는 boolean 타입만 사용할 수 있다.

 

5. instanceof

instanceof 연산자는 객체가 특정 클래스에 속하는지 아닌지를 확인할 수 있으며, 상속 관계까지 확인할 수 있다.

형변환 가능 여부를 확인하게 되며 결과값을 true 또는 false로 반환하게 된다.

 

6. assignment(=) operator

할당 연산자라고 부르는 assignment operator는 변수에 값을 할당하는데 사용한다.

할당 연산자의 왼쪽 피연산자는 변수에 해당되며 오른쪽 피연산자는 값이 된다. 오른쪽에 있는 값은 왼쪽에 있는 피연산자의 데이터 타입과 동일해야 할당이 가능하다. 타입이 동일하지 않은 경우 컴파일 에러가 발생한다.

 

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

Java SE 8부터는 자바에서는 화살표(ㅁ) 연산자를 사용해서 람다 표현식을 작성할 수 있다. 람다 표현식이란 간단히 말해 메소드를 하나의 식으로 표현하는 방법이며 람다 표현식으로 표현을 하면 클래스를 작성하고 객체를 생성하지 않아도 메소드를 사용할 수 있다.

 

자바에서는 클래스의 선언과 동시에 객체를 생성하므로 단 하나의 객체만을 생성할 수 있는 클래스를 익명 클래스라고 한다.따라서 자바에서 람다 표현식은 익명 클래스와 같다고 할 수 있다.

new Thread(new Runnable() {
    public void run() {
        System.out.println("전통적인 방식의 일회용 스레드 생성");
    }
}).start();

 

new Thread(()->{
    System.out.println("람다 표현식을 사용한 일회용 스레드 생성");
}).start();

 

이러한 람다 표현식은 메소드의 매개변수나 메소드의 결과값으로 반환될 수 있어 기존의 불필요한 코드를 줄여주고, 작성된 코드의 가독성을 높여주는 역할을 한다. 람다 표현식을 사용하여 자바에서도 함수형 프로그래밍을 할 수 있게된 것이다.

 

자바에서 람다 표현식을 작성할 때 유의해야 할 사항은 다음과 같다.

 

  • 매개변수의 타입을 추론할 수 있는 경우에는 타입을 생략할 수 있다.

  • 매개변수가 하나인 경우에는 괄호(())를 생략할 수 있다.

  • 함수의 몸체가 하나의 명령만으로 이루어진 경우에는 중괄호 ({})를 생략할 수 있다. (이때 세미콜론은 붙이지 않는다.)

  • 함수의 몸체가 하나의 return문으로만 이루어진 경우에는 중괄호({})를 생략할 수 없다.

  • return문 대신 표현식을 사용할 수 있으며, 이 때 반환값은 표현식의 결과값이다. (이때 세미콜론은 붙이지 않는다.)

TCP School, 람다 표현식, www.tcpschool.com/java/java_lambda_concept

 

유의해야 할 사항들을 반영한 람다표현식의 표현 방법은 다음과 같이 나타낼 수 있다.

 

  • (매개변수) -> {함수몸체}

  • () -> {함수몸체}

  • (매개변수) -> 함수몸체

  • (매개변수) -> {return O;}

함수형 인터페이스(functional interface)

람다 표현식을 사용할 때는 "참조변수의타입 참조변수의이름 = 람다 표현식" 문법을 사용해 람다 표현식을 저장하기 위한 참조 변수의 타입을 결정해야만 한다. 람다 표현식을 하나의 변수에 대입할 때 사용하는 참조 변수의 타입을 함수형 인터페이스라고 부른다.

 

함수형 인터페이스는 추상 클래스와는 달리 단 하나의 추상 메소드만을 가져야 하며 인터페이스 작성 시 @FunctionalInterface 어노테이션을 사용하여 함수형 인터페이스임을 명시할 수 있다. 자바 컴파일러는 함수형 인터페이스라고 명시된 인터페이스에 두 개 이상의 메소드가 선언되면 오류를 발생시키게 된다.

 

@FunctionalInterface
interface Calc { 
    public int min(int x, int y);

}

public class Lambda02 {
public static void main(String[] args){
        Calc minNum = (x, y) -> x < y ? x : y;
        System.out.println(minNum.min(3, 4));
    }
}

 

 

8. 삼항 연산자

 

삼항 연산자는 조건식 ? 피연산자1 : 피연산자2 와 같은 형태로 사용하는 연산자로 조건식의 연산 결과가 true 이면 결과는 피연산자1이고, 조건식의 연산결과가 false 이면 결과는 피연산자2이다. 삼항 연산자는 가벼운 로직을 처리할 때 다수 줄의 코드를 가독성이 좋게 짧은 줄로 바꿔줄 수 있어 사용된다.

 

9. 연산자 우선순위

프로그램을 실행할 때는 연산자의 연산 방향과 연산자 간의 우선순위가 정해져 있다. 어떤 연산자를 사용하느냐에 따라 연산의 방향과 우선순위가 정해지기 때문에 복잡한 연산식에서는 주의가 필요하다. 다음은 연산자의 연산 방향과 우선순위를 정리한 표이다.

 

10. Java 13. switch 연산자

Java 13에서부터는 이전 Java 12 Switch 연산자와 다르게 값을 리턴하는 yield 라는 키워드가 추가되어

Java 12에서 사용하던 value breaks 문법은 Java 13에서는 사용하지 않으며 대신에 yield 키워드를 사용하게 되었다.

 

// Java 12
// value breaks are superseded by Java 13 'yield' statements.
    private static int getValueViaBreak(String mode) {
        int result = switch (mode) {
            case "a":
            case "b":
                break 1;
            case "c":
                break 2;
            case "d":
            case "e":
            case "f":
                break 3;
            default:
                break -1;
        };
        return result;
    }
// Java 13
    private static int getValueViaYield(String mode) {
        int result = switch (mode) {
            case "a", "b":
                yield 1;
            case "c":
                yield 2;
            case "d", "e", "f":
                // do something here...
                System.out.println("Supports multi line block!");
                yield 3;
            default:
                yield -1;
        };
        return result;
    }
package com.mkyong.java13;

public class JEP354 {

    public static void main(String[] args) {

        System.out.println(getValueViaYield("a"));
        System.out.println(getValueViaYield("c"));
        System.out.println(getValueViaYield("e"));
        System.out.println(getValueViaYield("z"));

    }

    // Traditional switch
    private static int getValueBefore12(String mode) {
        int result;
        switch (mode) {
            case "a":
            case "b":
                result = 1;
                break;
            case "c":
                result = 2;
                break;
            case "d":
            case "e":
            case "f":
                result = 3;
                break;
            default:
                result = -1;
        }
        ;
        return result;
    }

    // Java 12, multiple comma-separated labels
    private static int getValueMultipleLabels(String mode) {
        int result;
        switch (mode) {
            case "a", "b":
                result = 1;
                break;
            case "c":
                result = 2;
                break;
            case "d", "e", "f":
                result = 3;
                break;
            default:
                result = -1;
        }
        ;
        return result;
    }

    // Java 13, value breaks are superseded by 'yield' statements
    // Java 12, switch expression returning value via break
    /*private static int getValueViaBreak(String mode) {
        int result = switch (mode) {
            case "a":
            case "b":
                break 1;
            case "c":
                break 2;
            case "d":
            case "e":
            case "f":
                break 3;
            default:
                break -1;
        };
        return result;
    }*/

    // Java 12, switch expression returns a value via label rules (arrow)
    private static int getValueViaArrow(String mode) {
        int result = switch (mode) {
            case "a", "b" -> 1;
            case "c" -> 2;
            case "d", "e", "f" -> {
                // do something here...
                System.out.println("Supports multi line block!");
                yield 3;
            }
            default -> -1;
        };
        return result;
    }

    // Java 13, switch expression returns a value via yield
    private static int getValueViaYield(String mode) {
        int result = switch (mode) {
            case "a", "b":
                yield 1;
            case "c":
                yield 2;
            case "d", "e", "f":
                // do something here...
                System.out.println("Supports multi line block!");
                yield 3;
            default:
                yield -1;
        };
        return result;
    }

}
Mkyong.com, Java 13 - Switch Expressions, mkyong.com/java/java-13-switch-expressions/

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함