본문 바로가기
스터디/[white-ship] 자바 스터디(study hale)

3주차 과제: 연산자

by doyoungKim 2021. 1. 2.

목표

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

학습할 것

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

마감일시

2020년 11월 28일 오후 3시까지.


 

0. 연산관련 용어

  • 연산: 수나 식을 일정한 규칙에 따라 계산하는 것이다.
  • 연산자: 연산을 수행하는 기호
  • 피연산자: 연산에 참여하는 변수나 상수

 

1. 자바 연산자와 연산자 우선 순위

연산기호

결합 방향

우선순위

[ ], .

->

1

expr++, expr--

<-

2

++expr, --expr, +expr, -expr

~, !, (type)

<-

3

*, /, %

->

4

+, -

->

5

<<, >>, >>>

->

6

<, >, <=, >=, instanceof

->

7

==, !=

->

8

&

->

9

^

->

10

!

->

11

&&

->

12

||

->

13

? expr : expr

<-

14

=, +=, -=, *=, /=, %=, &=

^=, !=, <<=, >>=, >>>=

<-

15

 

2. 산술 연산자 와 대입 연산자 (assignment operator)

  • 대입연산자: 오른쪽에 있는 값을 왼쪽에 있는 변수에 대입한다. (val = 20)
  • 산술연산자: 흔히 사칙연산이라고 부르기도 한다.

연산자

기능

결합 방향

=

연산자 오른쪽에 있는 값을 연산자 왼쪽에 있는 변수에 대입한다.

<-

+

두 피연산자의 값을 더한다.

->

-

왼쪽의 피연산자 값에서 오른쪽의 피연산자 값을 뺀다.

->

*

두 피연산자의 값을 곱한다.

->

/

왼쪽의 피연산자의 값을 오른쪽의 피연산자 값으로 나눴을 떄 얻게 되는 몫을 반환한다.

->

%

왼쪽의 피연산자 값을 오른쪽의 피연산자 값으로 나눴을 때 얻게 되는 나머지를 반환한다.

->

 

int val = 10 + 3 을 보면
우선순위가 높은 +( + 4순위, = 15순위) 연산부터 진행된다.
한 차례 연산 결과 int val = 13 이 된다. 그리고 나서 
val 이라는 변수에 대입 연산자에 의해 값이 할당된다.

예시

int number1 = 9;
int number2 = 4;

 // number1 + number2 = 13
System.out.println("number1 + number2 = " + (number1 + number2));

// number1 - number2 = 5
System.out.println("number1 - number2 = " + (number1 - number2)); 

// number1 * number2 = 36
System.out.println("number1 * number2 = " + (number1 * number2));

// number1 / number2 = 2
System.out.println("number1 / number2 = " + (number1 / number2)); 

// number1 & number2 = 1
System.out.println("number1 & number2 = " + (number1 % number2)); 

 

이 때 기존에 있던 변수에 저장된 값에 대해서

산술연산을 할 수 있는데 이것을 복합 대입연산자라고 한다.

number = number + 5; // 변수 number에 저장된 값이 5 증가한다.
number += 5;


여기서 흥미로운 점은 형변환 부분이다.

short shortNumber = 10;
shortNumber = (short) (shortNumber + 77L); // 형변환 필수 아니면 컴파일 에러
int intNumber = 3;
intNumber = (int) (intNumber * 3.3); // 형변환 필수 아니면 컴파일 에러
System.out.println(shortNumber);
System.out.println(intNumber);

shortNumber = 10;
shortNumber += 77L; // 자동 형변환
intNumber = 3;
intNumber += 3.3;  // 자동 형변환
System.out.println(shortNumber);
System.out.println(intNumber);


복합연산자를 사용하면 컴파일러가 알아서 해석해주기 때문에 명시적인 형 변환을 줄일 수 있는 장점이 있다.

 

​3. 비트 연산자

비트 연산자는 피연산자의 비트 열을 왼쪽 또는 오른쪽으로 이동시킨 결과를 반환하는 연산자이다. 이 연산자도 두 개의 피연산자가 필요한 이항 연산자이며 피 연산자는 모두 정수 이여야 한다.

연산자

기능

결합 방향

<<

* 피연산자의 비트 열을 왼쪽으로 이동

* 이동에 따른 빈 공간은 0으로 채움

n << 2;

n 의 비트 열을 두 칸 왼쪽으로 이동 시킨 결과 반환

->

>>

* 피연산자의 비트 열을 오른쪽으로 이동

* 이동에 따른 빈 공간이 음수의 경우 1, 양수의 경우 0으로 채움

n >> 2;

n 의 비트 열을 두 칸 오른쪽으로 이동 시킨 결과 반환

->

>>>

* 피연산자의 비트 열을 오른쪽으로 이동

* 이동에 따른 빈 공간은 0으로 채움

n >>> 2;

n 의 비트 열을 두 칸 왼쪽으로 이동 시킨 결과 반환

->

 

int number1 = 20;
System.out.println("(number1 >> 1) = " + (number1 >> 1)); // 10

 

>> 연산을 통해서 한 칸씩 오른쪽으로 밀 때마다 2로 나눈 결과를 반환한다.

 

4. 관계 연산자 

관계 연산자는 두 개의 피연산자 사이에서 크기 및 동등 관계를 따져주는 이항 연산자

연산자

기능

결합 방향

<

n1 < n2

n1 이 n2 보다 작은가?

->

>

n1 < n2

n1 이 n2 보다 큰가?

->

<=

n1 < n2

n1 이 n2 보다 같거나 작은가?

->

>=

n1 < n2

n1 이 n2 보다 같거나 큰가?

->

==

n1 < n2

n1 이 n2 보다 같은가?

->

!=

n1 < n2

n1 이 n2 보다 다른가?

->

질문의 결과가 진실이면 true 거짓이면 false 가 반환된다.

System.out.println("3 < 2 : " + (3 < 2)); // false
System.out.println("3 > 2 : " + (3 > 2)); // true
System.out.println("3 >= 3 : " + (3 >= 3)); // true
// 자동 형 변환 예시
System.out.println("3.0 == 3 : " + (3.0 == 3)); // true
// 자동 형 변환 예시
System.out.println("3.0 != 3 : " + (3 != 3)); // false

 

5. 논리 연산자

논리 연산자도 true 또는 false 를 반환하는 연산자로써 다음과 같다.

연산자

기능

결합 방향

&&

A && B

A와 B 모두 true 이면 연산 결과는 true

->

||

A || B

A 또는 B가 true 이면 연산 결과는 true

->

!

!A

연산결과가 true 이면 false, false 이면 true

->

간단하지만 주의 할 점이 있다.

Short-Circuit Evaluation (SCE) 이다.

int number1 = 0;
int number2 = 0;
boolean result;

// 1 
result = ((number1 += 10) < 0) && ((number2 += 10) > 0);
System.out.println("result = " + result);
System.out.println("number1 = " + number1);
System.out.println("number2 = " + number2);

// 2
result = ((number1 += 10) > 0) || ((number2 += 10) > 0);
System.out.println("result = " + result);
System.out.println("number1 = " + number1);
System.out.println("number2 = " + number2);

1.
result = false
number1 = 10
number2 = 0

2.
result = true
number1 = 20
number2 = 0

 

1번 같은 경우

(number1 += 10) < 0)

이 부분에서 이미 false 가 나와서 다음 연산 결과가 무엇이 나오든 false 이기 때문에 뒤의 부분 연산을 진행하지 않은 것이고

2번 같은 경우는

(number1 += 10) > 0)

 

이 부분에서 이미 true 가 나와서 다음 연산 결과가 무엇이 나오든 true이기 때문에 뒤의 부분 연산을 진행하지 않은 것이다.

이러한 이유 때문에 비교 연산자 부분에 대입 연산자를 같이 두지 않는 것이 바람직하다.

number1 += 10;
number2 += 10;
result = (number1 < 0) && (number2 > 0);

 

6. instanceof

참조변수가 참조하는 인스턴스의 '클래스'나 참조하는 인스턴스가 '상속하는 클래스'를 묻는 연산자이다.
예를 들면 다음과 같이 문장이 구성할 수 있다.

if (toast instanceof Bread)

toast 는 참조변수이고 Bread 는 클래스이다.

만약 toast 가 Bread를 참조하면 true 값이 나오고 아니면 false가 나온다.

예시

class Box {
    public void simpleWrap() { 
        System.out.println("Simple Wrapping");
    }
}

class PaperBox extends Box {
    public void paperWrap() {
        System.out.println("Paper Wrapping");
    }
}

class GoldPaperBox extends PaperBox {
    public void goldWrap() {
        System.out.println("Gold Wrapping");
    }
}

class Wrapping {
    public static void main(String[] args) {
        Box box1 = new Box();
        PaperBox box2 = new PaperBox();
        GoldPaperBox box3 = new GoldPaperBox();
        
        wrapBox(box1);
        wrapBox(box2);
        wrapBox(box3);
    }

    public static void wrapBox(Box box) {
        if(box instanceof GoldPaperBox) {
            ((GoldPaperBox)box).goldWrap();
        }
        else if(box instanceof PaperBox) {
            ((PaperBox)box).paperWrap();
        }
        else {
            box.simpleWrap();
        }
    }
}

 

부모 클래스 타입으로 들어온 매개변수가 실제로는 참조하는 타입은 자식이고 어떤 자식인지 알아야 형변환하여 메소드를 호출하는데
이때 instanceof 를 사용해서 쉽게 해결 할 수 있다.

결론적으로, "연산자 instanceof는 명시적 형 변환의 가능성을 판단해주는 연산자이다."

추후에 상속을 유지하면서 메소드를 오버라이딩 기반으로 다음과 같이 리팩토링 할 수 있다.

class Box {
    public void wrap() { 
        System.out.println("Simple Wrapping");
    }
}

class PaperBox extends Box {
    public void wrap() {
        System.out.println("Paper Wrapping");
    }
}

class GoldPaperBox extends PaperBox {
    public void wrap() {
        System.out.println("Gold Wrapping");
    }
}

class Wrapping {
    public static void main(String[] args) {
        Box box1 = new Box();
        PaperBox box2 = new PaperBox();
        GoldPaperBox box3 = new GoldPaperBox();
        
        wrapBox(box1);
        wrapBox(box2);
        wrapBox(box3);
    }

    public static void wrapBox(Box box) {
        box.wrap();
    }
}

 

7. 3항 연산자

3항 연산자는 A ? B : C 형태로 A가 true 이면 B false 이면 C 값을 반환한다.

C 자리에는 다른 3항 연산자가 들어와도 된다.

if else 구문을 한줄로 간단하게 처리할 때 사용하면 유용하다.

boolean isLunchTime = false;
boolean isDinnerTime = false;

//삼항 연산자
String time = isLunchTime ? "점심식사" : isDinnerTime ? "저녁식사" : "아침식사";

//if문
String time;
if (isLunchTime) {
    time = "점심식사";
} else if (isDinnerTime) {
    time = "저녁식사";
} else {
    time = "아침식사";
}

 

8. Java 13. switch 연산자

자바 13 부터 break 자리에 yeild 키워드를 이용하여 리턴을 할 수 있게 되었다.

 

9. 화살표 연산자

자바는 객체지향 언어이지만, "기능 하나를 정의해야 하는 상황" 을 자주 접하게 된다.
functional 정의, functional interface, @FunctionalInterface가 생기었다.
이 함수형 인터페이스를 기반으로 람다식을 작성 할 수 있다.

매개변수가 있고 반환하지 않는 람다식

interface Printable {
    void print(String s);    // 매개변수 하나, 반환형 void
}

class OneParamNoReturn {
    public static void main(String[] args) {
        Printable p;

        p = (String s) -> {System.out.println(s);};    // 줄임 없는 표현
        p.print("Lambda exp one.");

        p = (String s) -> System.out.println(s);    // 중괄호 생략
        p.print("Lambda exp two.");
        
        p = (s) -> System.out.println(s);    // 매개변수 형 생략
        p.print("Lambda exp three.");

        p = s -> System.out.println(s);    // 매개변수 소괄호 생략
        p.print("Lambda exp four.");
    }
}

 

interface Calculate {
    void cal(int a, int b);    // 매개변수 둘, 반환형 void
}

 

class TwoParamNoReturn {
    public static void main(String[] args) {
        Calculate c;
        c = (a, b) -> System.out.println(a + b);
        c.cal(4, 3); // 이번엔 덧셈이 진행

        c = (a, b) -> System.out.println(a - b);
        c.cal(4, 3); // 이번엔 뺄셈이 진행

        c = (a, b) -> System.out.println(a * b);
        c.cal(4, 3); // 이번엔 곱셈이 진행
    }
}

 

매개변수가 있고 반환 하는 람다식

interface Calculate {
    int cal(int a, int b); // 값을 반환하는 추상 메소드
}

class TwoParamAndReturn {
    public static void main(String[] args) {
        Calculate c;
        c = (a, b) -> { return a + b; };
        System.out.println(c.cal(4, 3));
        
        c = (a, b) -> a + b; // return 과 중괄호 생략 가능
        System.out.println(c.cal(4, 3));
    }
}

 

매개변수가 없는 람다식

interface Generator {
    int rand();    // 매개변수 없는 메소드
}

class NoParamAndReturn {
    public static void main(String[] args) {
        Generator gen = () -> {
            Random rand = new Random();
            return rand.nextInt(50);
        };

        System.out.println(gen.rand());
    }
}

 

마지막으로 화살표 연산자를 사용할 때, 매개변수 개수와 타입이 같다면 더블클론 형태로 바꿀 수 있다.

예시

@FunctionalInterface
public interface StringToDog {
    public String convert(String name, String species, int price);
}

public class Dog {
    private String name;
    private String species;
    private int price;
    public static String introduce(String name, String species, int price) {
        return name + " : " + species + " : " + price;
    }
}

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class DoubleColonTest {
    public static void main(String[] args) {
        StringToDog stringToDog1 = (name, species, price) -> Dog.introduce(name, species, price);
        StringToDog stringToDog2 = Dog::introduce;
        System.out.println(stringToDog1.convert("개똥이", "믹스", 100));
        System.out.println(stringToDog2.convert("누렁이", "믹스", 1000));
    }
}

 

728x90

댓글