반응형

 

public class Practice implements A, B {
	...
}

interface A {
    default void print() {
        System.out.println("hi");
    }
}

interface B {
    default void print() {
        System.out.println("hello");
    }
}

위처럼 Practice가 구현하는 A, B 인터페이스가 동일한 시그니쳐의 default 메서드를 가지면 어떻게 될까? 라는 궁금증이 생겼습니다.

 

확인해보니 아래와 같은 에러가 발생합니다.

A와 B로부터 연결되지 않은 default 메서드들을 상속받았다는 것입니다.

 

public class Practice implements A, B {

    @Override
    public void print() {
        A.super.print();
    }
}

위와 같이 Overrinding을 통해 해결해야 합니다.

 

public class Practice implements A, B {

    @Override
    public void print() {
        A.super.print();
    }
}

interface A {
    default void print() {
        System.out.println("hi");
    }
}

interface B {
    default int print() {	// 반환 타입만 다름
        System.out.println("hello");
    }
}

 위처럼 인자는 같은데 반환타입만 다른 경우는, 충돌이 해결되지 않습니다.

 

public class Practice implements A, B {
    
}

interface A {
    default void print() {
        System.out.println("hi");
    }
}

interface B {
    default void print(int a) {
        System.out.println(a);
    }
}

위처럼 (반환타입과 관계 없이) 인자가 다른 경우는, Overring을 하지 않아도 됩니다.

클래스 생성 시에 Overloading이 되는 것으로 생각됩니다.

 


Java는 클래스의 다중 상속을 지원하지 않는데, Interface의 다중 상속(구현)은 가능합니다.

이는 Interface가 메서드의 구현이 없는 껍데기만 존재하기 때문에, 다중 상속에서 발생할 수 있는 충돌 문제로부터 안전하다고 판단했기 때문인데요,

 

편리함을 위해 추가한 Interface의 Default 메서드가 충돌의 원인이 될 수 있다는 것을 알았습니다.

편리함에는 항상 대가가 따르는 것 같습니다.

 

 

 

 

 

 

 

반응형
반응형

Null을 왜 사용하지 말아야 할까?

Null의 개념을 최초로 도입한 토니 호어(Tony Hoare)

null 참조를 만든 것은 Billion-dallor mistake(1조 짜리 실수)라고 했습니다.

Null을 사용함으로 인해, 무수한 에러들이 발생했기 때문입니다.

출처 : Sapienz: Multi-objective Automated Testing for Android Applications(University College London)

위 그래프는 안드로이드 앱의 결함 원인 통계입니다. NullPointer 에러로 인한 결함이 2위이고, Native crash 원인 내부에도 NullPointer로 인한 결함이 있을 수 있다고 합니다.

 

Null을 반환하는 API가 있다고 하면, 그것을 사용하는 사람이 꼼꼼하게 Null 가능성에 대해 점검을 해야 합니다. 하지만 모든 객체에 대해 Null 가능성을 점검하는 일은 쉽지 않습니다.

 

두 가지 방법으로 이를 해결할 수 있는데요, (더 많은 방법이 존재하긴 합니다.)

  1. Null을 안전하게 다루기
  2. Optional을 사용하기

하나씩 살펴보겠습니다.

 

 

Null을 안전하게 다루기

Null을 반환하지 말자.

  • null 반환 대신 예외 발생
  • Collection의 경우 emptyList, emptySet 등 사용
  • Null 객체를 구현 (Null object pattern 사용 / 참고 링크(클릭))
  • Optional 반환 → 아래에서 알아볼 예정

 

Null의 범위를 지역에 제한하라.

  • 부득이하게 null을 사용해야 한다면, 지역 변수(메서드, 생성자 내부)로만 사용한다.
    • 불가피하다면 클래스 영역에서만 사용한다.

 

초기화를 명확히 하라.

  • 실행 시점에 초기화되지 않은 필드가 없게 하라.
  • 실행 시점에 null인 필드는, 초기화되지 않았다는 의미가 아니라, 값이 없다는 의미여야 한다.

그 외에도 많은 내용들이 있지만, 개인적으로 와닿고 이해되는 부분을 정리해보았습니다.

 

 

Optional 올바르게 사용하기

Optionalnull일 수도 있는 값을 wrapping한 클래스입니다.

 

Optional 사용의 기본 원칙

1. Optional은 클래스의 필드로 사용하면 안 됩니다.

정확히는 사용할 수는 있으나, 설계 목적에 반하는 사용입니다.

Optional은 반환 값이 null일 수도 있음을 알려주기 위해 만들어졌습니다.

그래서 데이터를 직렬화(Serialize)할 수가 없습니다.

 

2. Optional은 메서드, 생성자의 인자로 사용하면 안 됩니다.

메서드나 생성자에서는 Optional을 받든 아니든 null 체크를 반드시 하는 것이 안전합니다.

굳이 비용이 비싼 Optional을 사용할 필요가 없습니다.

정리하면, Optional은 반환 타입으로만 사용하는 것이 옳습니다.

 

Optional 메서드

Optional에는 다양한 메서드가 존재합니다.

그에 대해 간단히 용법을 설명해보겠습니다.

  • empty() : null을 담고 있는 Optional 인스턴스를 생성합니다. (Optional 내부적으로 미리 생성해놓은 싱글톤 인스턴스입니다.)
  • of(T value) : Optional 인스턴스를 생성해, 인자로 받은 값을 저장합니다. null이 입력되면 NPE(NullPointerException)이 발생합니다.
  • ofNullabe(T value) : of와 동일하나, null이 입력될 수 있습니다.
  • get() : 값이 있다면 반환하고, 없다면 NoSuchElementException이 발생합니다. 그냥 안 쓰는 것이 좋습니다.
  • orElse(T value) : 값이 있다면 반환하고, 없다면 orElse() 메서드 내부의 값을 반환합니다.
  • orElseGet(Supplier<? extends T> other) : 값이 있다면 반환하고, 없다면 인자로 받은 Supplier를 실행합니다. 함수를 실행한다는 뜻입니다. (orElselazy한 버전이라고 생각해주시면 됩니다.)
  • orElseThrow(Supplier<? extends X> exceptionSupplier) : 값이 있다면 반환하고, 없다면 Supplier로 넘어온 예외를 던집니다.
  • isPresent() : 값이 있다면 true, 없다면 false를 반환합니다.
  • isEmpty() : isPresent()와 반대입니다. 값이 없을 때 true를 반환, 있다면 false를 반환합니다.
  • ifPresent(Consumer<? super T> consumer) : 값이 존재하는 경우 consumer 로직을 실행합니다.
  •  

Tip1. orElse(new Object()) 보다, orElseGet(() → new Object())를 사용한다.

orElse(new Object()) 를 사용하는 경우, orElse에 도달하기 전에 new Object() 명령이 실행됩니다.

하지만, orElseGet(() → new Object())의 경우 orElse에 도달하는 경우 즉, Optional 내부에 값이 없는 경우에만 Supplier가 실행되므로 객체 생성 비용을 아낄 수 있습니다.

 

Tip2. 단지 값을 얻을 목적이라면 Optional 대신 null을 비교한다.

// X
return Optional.ofNullable(status).orElse(READY);

// O
return status != null ? status : READY;

 

Tip3. primative 자료형의 경우 OptionalInt, OptionalLong, OptionalDouble을 사용한다.

자료형을 다시 Boxing, Unboxing하지 말고, 이미 준비된 클래스를 사용하는 것이 좋습니다.

 

 

Optional의 활용

Optional은 0~1개의 원소를 가지고 있는 Stream으로 생각할 수 있습니다.

직접 구현, 상속 관계가 있는 것은 아니지만, 사용 방법이나 사상이 유사하기 때문입니다. (함수형 인터페이스 구조)

따라서 map(), flatmap(), filter() 등의 메서드를 사용할 수 있습니다.

public Optional<Name> getNameIfLengthOverStandard(Player player, int standard) {
    return Optional.ofNullable(player)
            .filter(player -> player.getName().length() > standard)
            .map(Order::getName);
}

위처럼, 특정 조건을 만족하는 경우에는 값을, 아닌 경우 null을 담고 있는 Optional을 반환할 수도 있습니다.

과정에서 map()을 통해 Player의 필드인 Name으로 자료형을 치환했습니다.

 

 


Null의 위험성과 올바르게 사용하는 방법,

그리고 Optional을 통해 Null-safe한 코드를 작성하는 방법을 알아보았습니다.

Optional은 생성 비용이 비싸므로, 최후의 수단으로 사용하는 것이 좋다고 하네요.

이 점 공유드리며, 글을 마치겠습니다.

긴 글 읽어주셔서 감사합니다.

 

 

 

 

참고 자료

반응형
반응형

 

 

 

String.join 메서드를 사용하면

문자열 리스트나 배열(List<String>, String[])의 각 요소들을

특정 문자로 이어붙일 수 있습니다.

 

 

 

 

String 객체 이어붙이기

// String 객체 이어붙이기
String str1 = "감자";
String str2 = "고구마";

String strJoined = String.join(", ", str1, str2);

System.out.println(strJoined); // 감자,고구마

 

String 리스트 이어붙이기

// String 객체 이어붙이기
ArrayList foods = new ArrayList();
list.add("떡볶이");
list.add("마라탕");

String strJoined = String.join(", ", foods);

System.out.println(strJoined); // 떡볶이, 마라탕

 

String 배열 이어붙이기

// String 객체 이어붙이기
String[] foods = new String[2]
foods[0] = "떡볶이";
foods[1] = "마라탕";

String strJoined = String.join(", ", foods);

System.out.println(strJoined); // 떡볶이, 마라탕

 

 

 

 

 

 

반응형
반응형

 

 

 

프로그램을 작성하다 보면

예외를 처리한 후, 예외가 발생된 메서드를 다시 실행해야하는 경우가 있습니다.

그것을 수행하는 몇 가지 방법에 대해 정리해보고자 합니다.

 

예외 처리의 기본적인 원리나,

예외 처리 후 프로그램을 종료하는 로직에 대한 설명은

아래 아래클을 참고해주세요.

 

[Java] 간단한 예외처리(try catch) 원리 (feat. throws IllegalArgumentException은 왜 빨간 줄이 안 생길까?)

예외를 처리하다 보면, 아래와 같은 에러 문구를 쉽게 볼 수 있습니다. 바로 Unhandled exception 입니다. 예외 발생 예외의 종류는 다양하고, 예외가 발생하는 코드도 다양합니다. 본 아티클에서는,

woojin.tistory.com

 

 

 

 


문제 상황 1번 - 예외 처리 후 재실행

비즈니스 요구사항
1. 숫자를 입력 받고, 2를 곱한 수를 출력한다.
2. 숫자가 아닌 문자를 입력 받으면 [ERROR]로 시작하는 에러 메시지를 출력하고, 다시 입력 받는다.
// main 메서드를 호출하는 Practice 클래스
public class Practice {
    public static void main(String[] args) {
        Study study = new Study();
        study.multiple();
    }
}


// 비즈니스 로직을 구현한 Study 클래스
public class Study {
    public void multiple() {
        int number = readNumber();
        System.out.printf("%d에 2를 곱하면 %d 입니다.", number, number*2);
    }

    private int readNumber() {
        System.out.print("숫자를 입력해주세요 : ");
        Scanner scanner = new Scanner(System.in);
        int number = scanner.nextInt();
        return number;
    }
}

위의 코드는, 숫자를 입력 받아 2를 곱한 출력을 보여주는 로직을 구현한 것입니다.

출력은 아래 사진과 같습니다.

 

그런데, 숫자가 아닌 문자를 입력한다면 어떻게 될까요?

InputMismatchException이 발생하고 프로그램이 종료됩니다.

 

비즈니스 요구사항을 만족하려면

에러 메시지를 출력하고, 입력을 다시 받아야 합니다.

아래와 같이 코드를 수정하겠습니다.

 

// 재귀적 호출
public class Study {
    public void multiple() {
        try {
            int number = readNumber();	// 1번
            System.out.printf("%d에 2를 곱하면 %d 입니다.", number, number*2);	// 2번
        } catch (InputMismatchException e) {	// 3번
            System.out.println("[ERROR] 숫자를 입력해주세요.");	// 4번
            multiple();	// 5번
        }
    }

    private int readNumber() {
        System.out.print("숫자를 입력해주세요 : ");
        Scanner scanner = new Scanner(System.in);
        int number = scanner.nextInt();
        return number;
    }
}

예외는 readNumber()에서 발생합니다.

그것을 호출하는 multiple()try-catch를 적용하고,

재귀적으로 multiple()을 다시 호출했습니다.

 

즉, 1번에서 예외가 발생하면

2번을 건너뛰고

3번에서 예외를 catch합니다.

그리고 4번, 5번 문장이 실행됩니다.

 

하지만 재귀적 호출 방식에는 치명적인 단점이 있습니다.

메모리에 스택 방식으로 메서드 호출 명령이 저장되지만,

호출된 메서드가 종료되지 않으면 메모리에 명령이 계속 쌓이게 되어

스택오버플로우(SOF) 현상이 발생할 수 있습니다.

 

사실 위의 예시 코드는 재귀적 호출 시 별도의 연산을 하지 않고,

함수를 호출하기만 하는 방식입니다.

그래서 SOF의 부담이 적기는 하지만, 호출된 깊이만큼 메모리에 스택이 쌓이는 것은 마찬가지입니다.

호출되는 로직이 변경되는 경우도 있을 수 있습니다.

 

따라서 재귀적 호출의 위험을 없애기 위해,

반복문을 통해서도 문제를 해결할 수 있습니다.

// 반복문 사용
public class Study {
    public void multiple() {
        while (true) {
            try {
                int number = readNumber();
                System.out.printf("%d에 2를 곱하면 %d 입니다.", number, number * 2);
                break;
            } catch (InputMismatchException e) {
                System.out.println("[ERROR] 숫자를 입력해주세요.");
            }
        }
    }

    private int readNumber() {
        System.out.print("숫자를 입력해주세요 : ");
        Scanner scanner = new Scanner(System.in);
        int number = scanner.nextInt();
        return number;
    }
}

catch문에서 multiple()을 다시 호출하지 않고,

try문이 온전히 실행되어 break 될 때까지

try-catch문을 반복합니다.

 

 

multiple()이 아닌 readNumber()에서 try-catch를 할 수도 있습니다.

 

다만, readNumber()의 반환 타입은 int이기 때문에

catch문에서는 두 가지 중 하나를 실행해야 합니다.

1. int를 반환한다.

2. Throwble 객체를 throw 한다.

 

입력을 다시 받지 않고, catch 문에서 int를 반환하는 것은 어색합니다.

Throwble 객체를 throw한다면, 결국 상위 메서드에서 try-catch를 통해 예외를 처리해야 합니다.

따라서 readNumber()가 아닌 상위 메서드 multiple()에서 예외 처리를 진행했습니다.

 

 

 

 

 

문제 상황 2번 - 특정 예외를 발생시킨 후 재실행

비즈니스 요구사항
1. 숫자를 입력 받고, 2를 곱한 수를 출력한다.
2. 숫자가 아닌 문자를 입력 받으면 IllegalArgumentException을 발생시키고, [ERROR]로 시작하는 에러 메시지를 출력한 후, 다시 입력 받는다.

이번에는 특정 예외를 발생하라는 요구사항이 추가되었습니다.

기존에는 readNumber()scanner.nextInt()라는 구문에서 InputMismatchException이 발생했습니다.

이번에는 이를 IllegalArgumentException으로 바꿔서 예외를 발생시켜보겠습니다.

 

// 재귀적 호출
public class Study {
    public void multiple() {
        try {
            int number = readNumber();
            System.out.printf("%d에 2를 곱하면 %d 입니다.", number, number * 2);
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
            multiple();
        }
    }

    private int readNumber() {
        try {
            System.out.print("숫자를 입력해주세요 : ");
            Scanner scanner = new Scanner(System.in);
            int number = scanner.nextInt();
            return number;
        } catch (InputMismatchException e) {
            throw new IllegalArgumentException("[ERROR] 숫자를 입력해주세요.");
        }

    }
}

이번에는 readNumber() 내부에서 발생하는 InputMismatchExceptioncatch한 후 IllegalArgumentException을 발생시켰습니다.

그리고 상위 메서드인 multiple에서 그것을 catch한 후, 다시 multiple을 재귀적으로 호출합니다.

 

 

이 또한, 재귀적 호출로 인한 SOF의 위험에서 벗어나기 위해

반복문을 사용할 수 있습니다!

 

위의 문제 상황 1번을 참고하여, 직접 반복문으로 수정해보신다면

예외 처리에 대한 이해도가 높아지시리라 생각됩니다..만

 

아래에 반복문으로 수정한 코드를 공유드립니다.

 

 

// 반복문 사용
public class Study {
    public void multiple() {
        while (true) {
            try {
                int number = readNumber();
                System.out.printf("%d에 2를 곱하면 %d 입니다.", number, number * 2);
                break;
            } catch (IllegalArgumentException e) {
                System.out.println("[ERROR] 숫자를 입력해주세요.");
            }
        }
    }

    private int readNumber() {
        try {
            System.out.print("숫자를 입력해주세요 : ");
            Scanner scanner = new Scanner(System.in);
            int number = scanner.nextInt();
            return number;
        } catch (InputMismatchException e) {
            throw new IllegalArgumentException("[ERROR] 숫자를 입력해주세요.");
        }

    }
}

 

 

 

 

 


이렇게 예외가 발생했을 때, 예외가 발생한 메서드를 반복하는 로직을 구현해보았습니다.

 

1. 재귀적 호출

2. 반복문 사용

 

위의 두가지 방법으로 로직을 구현할 수 있었는데,

각각의 장단점이 있습니다.

 

재귀적인 호출은 반복문을 사용하지 않아 들여쓰기가 깊어지지 않는다는 장점이 있습니다.

또한 메모리에 명령 호출 스택이 쌓여, SOF(Stack OverFlow)가 발생할 수 있습니다.

반복문은 들여쓰기가 깊어지지만, 메서드 내부에서 반복을 원하는 부분만 반복할 수 있습니다.

 

가독성은 재귀적 호출이, 성능은 반복문 사용이 낫다고 볼 수 있습니다.

 

 

 

우리 인생도 수많은 예외가 발생하곤 합니다.

능숙하게 처리하고 목표를 이뤄내는 법을 익혀야겠다는 생각을 합니다.

 

감사합니다.

 

 

 

 

 

 

 

 

 

반응형
반응형

 

 

 

 

예외를 처리하다 보면, 아래와 같은 에러 문구를 쉽게 볼 수 있습니다.

바로 Unhandled exception 입니다.

 

 

 

예외 발생

예외의 종류는 다양하고, 예외가 발생하는 코드도 다양합니다.

본 아티클에서는, 어떤 코드에 의해 예외가 발생하는 상황을

throw new Exception(); 이라는 코드로 대체하겠습니다.

public void firstMethod() {
    System.out.println("First");
    secondMethod();
}

private void secondMethod() {
    System.out.println("Second");
    throw new Exception();
}

위처럼 예외가 발생되었는데, 그것을 처리하는 코드가 없을 때

Unhandled exception 이라는 예외가 발생하고, 컴파일에 실패합니다.

 

 

 

 

예외를 처리

public void firstMethod() {
    secondMethod();
    System.out.println("First");
}

private void secondMethod() {
    try {
        System.out.println("Second");
        throw new Exception();
    } catch (Exception e) {
        System.out.println(e);
    }
}

try catch를 이용해서 Exception을 처리했습니다.

try에서 Exception이 발생하자, 즉시 catch에서 처리됩니다.

그리고 다음 로직으로 이동합니다.

 

 

그런데, 예외가 발생 시 프로그램 동작이 멈춰야할 때가 있습니다.

 

위의 코드에서,

예외가 발생하면 "First"라는 문자열이 출력되지 않아야 한다고 가정해보겠습니다.

 

그럴 때는, thorws를 이용해

예외가 발생하는 메서드(secondMethod)에서

상위 메서드(firstMethod)로 예외를 던지고,

상위에서 처리(catch)하면 됩니다.

public void firstMethod() {
    try {
        secondMethod();
        System.out.println("First");
    } catch (Exception e) {
        System.out.println(e);
    }
}

private void secondMethod() throws Exception {
    try {
        System.out.println("Second");
        throw new Exception();
    } catch (Exception e) {
        throw e;
    }
}

 

 

로직을 자세하게 정리하면 아래와 같습니다.

(풀어써서 길어졌지만 그만큼 간단하니, 이해가 어려우시다면 천천히 읽어보시길 추천드립니다.)

 

1. secondMethod에서 Exception 발생

2. secondMethod 내부에서 Exception 처리(catch)

3. catch 문에서 새로운 Exception 발생

4. throws 키워드 사용하여, secondMethod를 호출한 상위 메서드(firstMethod)로 예외 던지기

5. firstMethod에서 Exception catch

6. System.out.println(e); 로 예외 로그 출력

7. 다음 코드인 System.out.println("First")는 실행되지 않음

 

이렇게 예외가 발생되면 프로그램이 종료되도록 로직을 수정해보았습니다.

(이는 컴파일 후 프로그램 동작 중 발생하는 런타임 에러에 의한 비정상적인 종료와는 다릅니다.)

 

 

 

 

throws하지 않아도 되는 예외? (IllegalArgumentException 등..)

위에서 사용한 Exception은 최상위 예외입니다.

그 아래에는 IllegalArgumentException, IOException 등 다양한 예외가 있습니다.

 

그런데, 일부 예외는 throws를 하지 않아도

Unhandled exception가 발생하지 않습니다.

 

public void firstMethod() {
    try {
        secondMethod();
        System.out.println("First");
    } catch (Exception e) {
        System.out.println(e);
    }
}

private void secondMethod() {
    System.out.println("Second");
    throw new IllegalArgumentException();
}

위 코드에서, secondMethodIllegalArgumentException를 발생시키지만

thorws도 하지 않고, catch도 하지 않습니다.

Exception이었다면 Unhandled exception라는 문구가 발생했을 상황인데요,

 

그 이유는 IllegalArgumentException런타임 예외(또는 Uncheck Exception)이기 때문입니다.

IOException과 같은 컴파일 예외(Check Exception)는 처리가 강제됩니다.

그렇지 않으면 컴파일을 실행할 수 없습니다.

 

하지만 IllegalArgumentException과 같은 런타임 예외는, 처리가 강제되지 않습니다.

프로그램 실행 도중 예외가 발생하면, 프로그램이 비정상 종료됩니다.

 

 

비록 처리가 강제되지는 않지만,

특정 예외가 발생할 수 있다는 것을 설명하기 위해서

throws 키워드를 사용하는 것이 어떨까 하는 것이 개인적인 생각입니다.

 

 

 

 

 

 

 

참고 자료 : https://stackoverflow.com/questions/50640610/how-to-use-throws-illegalargumentexception-in-method-declaration

반응형
반응형

 

헷갈리는 클래스 영역의 변수 용어에 관해 정리해보고자 합니다.

 

 

멤버 변수 : 클래스 영역에 선언된 변수

클래스 변수 : 멤버 변수 중 static이 붙은 변수로, 해당 클래스의 인스턴스 모두가 공유

인스턴스 변수 : 멤버 변수 중 static이 붙지 않은 변수로, 해당 인스턴스만 접근 가능한 변수

 

(메서드 내부의 변수는 지역 변수이고, 자바에는 전역 변수의 개념이 없다고 합니다.

관련 링크 -> https://shm-m.github.io/blog/global_variable)

 

 

 


예제 코드를 통해 살펴보겠습니다.

import java.util.ArrayList;
import java.util.List;

public class Chef {
    private static final String COMPLETE_COOK_ANNOUNCEMENT = " 준비가 완료되었습니다.";
    private static final String COMPLETE_MENUS_ANNOUNCEMENT = "완성된 요리 : ";
    private static List<String> dishes = new ArrayList<>();
    private int countOfMe = 0;

    public void cook(String menu) {
        System.out.println(menu + COMPLETE_COOK_ANNOUNCEMENT);
        dishes.add(menu);
        this.countOfMe += 1;
    }
    
    public void informCountMadeMyMe() {
        System.out.println(this.countOfMe);
    }

    public void informWholeCompleteMenus() {
        System.out.println(COMPLETE_MENUS_ANNOUNCEMENT + dishes);
    }

}

Chef 클래스는 음식을 요리하고,

해당 요리사가 만든 음식 개수와,

총 준비된 메뉴 리스트 알려줍니다.

여기서 변수의 종류를 정리해보면,

 

멤버 변수 : COMPLETE_COOK_ANNOUNCEMENT, COMPLETE_MENUS_ANNOUNCEMENT, dishes, countOfMe

클래스 변수 : COMPLETE_COOK_ANNOUNCEMENT, COMPLETE_MENUS_ANNOUNCEMENT, dishes

인스턴스 변수 : countOfMe

 

와 같습니다.

 

 

아래와 같이 main함수를 실행해보겠습니다.

public class Practice {
    public static void main(String[] args) {
        Chef woojin = new Chef();
        Chef paul = new Chef();

        woojin.cook("밥");
        paul.cook("국");

        woojin.informCountMadeMyMe();
        paul.informCountMadeMyMe();

        woojin.informWholeCompleteMenus();
        paul.informWholeCompleteMenus();
    }
}

 

woojinpaul이 각각 밥과 국을 요리했습니다.

그리고 각자가 만든 요리 개수 묻습니다.

마지막으로 woojin. paul은 전체 준비된 메뉴 리스트를 묻습니다.

 

위와 같이 출력됨을 확인할 수 있습니다.

 

Chef 클래스의 countOfMe 변수(인스턴스 변수)는 각 인스턴스에만 영향을 받았습니다.

 

하지만 woojin이 준비한 밥, paul이 준비한 국 모두 dishes라는 클래스 변수에 추가되었습니다.

dishes 변수는 모든 인스턴수에게 영향을 받았음을 알 수 있습니다.

 

 

멤버 변수와 클래스 변수, 인스턴스 변수의 차이와 활용에 대해 알아보았습니다.

 

 

 

 

 

 

 

 

반응형
반응형

 

 

do while문을 사용하는 방법과, 왜 사용하는지에 대해 정리해보고자 합니다.

 

 

 

while문의 사용

while문은 조건식이 참인 경우 반복을 지속하는 반복문입니다.

while(true) {
	System.out.println("hi");
}
// hi 무한 출력

int count = 0;
while(count < 3) {
	System.out.println(count);
    count += 1;
}
// 0 1 2 출력

 

 

 

 

do-while문의 사용

do {
	System.out.println("hi")
} while(true)
// hi 무한 출력

int count = 0;
do {
	System.out.println(count);
    count += 1;
} while(count < 3)
// 0 1 2 3 출력

위의 while문 코드를 그대로 do while문으로 변경한 코드입니다.

0 1 2 가 출력된 while문이,

do while문에서는 0 1 2 3이 출력됨을 알 수 있습니다.

 

 

그렇다면 do while문을 왜 사용하는 것일까요..

그 이유에 대해 정리해보겠습니다.

 

 

 

 

do-while문을 사용하는 이유

1, 4가 이미 들어있는 numbers라는 리스트가 있습니다.

19 사이의 랜덤 숫자를 뽑아 numbers에 넣고 싶은데,

이미 들어 있는 숫자는 안 된다고 하겠습니다.

 

예제 코드 - while문 사용)

class practice {
    List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 4));
    
    int randomNumber = 0;
    boolean isIn = true;

    while (isIn) {
        randomNumber = Randoms.pickNumberInRange(1, 9);	// 1과 9 사이 숫자를 랜덤 반환
        isIn = number.contain(randomNumber);	// randomNumber가 numbers에 이미 들어 있는지 여부
    }
	numbers.add(randomNumber);
}

while문으로 number에 들어있지 않은 숫자를 뽑을 때까지

randomNumber에 새로운 수를 가져오는 식으로 구현했습니다.

 

그런데, while의 반복이 지속되는 조건(isIn)은 number에 이미 randomNumber가 들어있다는 것입니다.

그런데 randomNumber를 뽑기도 전에 isIntrue라는 것은 코드의 가독성 측면에서 좋지 않습니다.

 

예제 코드의 수정 - do while 적용)

 

class practice {
    List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 4));
    
    int randomNumber = 0;
    boolean isIn;

    do {
        randomNumber = Randoms.pickNumberInRange(1, 9);	// 1과 9 사이 숫자를 랜덤 반환
        isIn = number.contain(randomNumber);	// randomNumber가 numbers에 이미 들어 있는지 여부
    } while(isIn)
	numbers.add(randomNumber);
}

위처럼 do while문을 사용하면,

isIn에 초기값을 설정하지 않아도 되고,

자연스러운 흐름으로 코드를 구현할 수 있습니다.

 

 

이처럼,

"어떤 행동을 최소 1회 수행해야만 -> 반복 여부가 결정"되는 경우에는

do while문을 쓰는 것이 적절합니다.

 

 

 

 

 

 

 

반응형
반응형

 

 

java.lang.assertionerror: expecting code to raise a throwable.

 

위와 같은 에러가 발생했습니다.

 

이는 테스트 코드 중 예외 처리를 테스트하는 부분에서 발생하는 에러로,

에러가 발생되지 않았다는 뜻입니다.

 

 

그런데 분명 적절한 에러를 throw 했음에도 해당 에러가 발생하였습니다.

 

 

여러 번 문장을 읽어보다가,

try-catch 문을 삭제했더니 해결되었습니다.

 

catch를 통해 에러를 잡아서 처리해버렸기 때문에,

실제로는 에러가 발생하지 않았던 것입니다.

 

테스트 코드에서는 에러가 throw 되기를 기다리고 있었으니,

해당 오류가 발생한 것이었습니다.

 

 

감사합니다.

 

 

 

 

 

 

 

반응형

+ Recent posts