[java] 구문 나쁜 관행을 만족시키기 위해 return 문이 있습니까?

다음 코드를 고려하십시오.

public Object getClone(Cloneable a) throws TotallyFooException {

    if (a == null) {
        throw new TotallyFooException();
    }
    else {
        try {
            return a.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
    //cant be reached, in for syntax
    return null;
}

return null;예외가 잡힐 수 있기 때문에 그러나 우리는 이미 널 (null)이 있다면 검사 (우리는 우리가 복제 지원을 호출하는 클래스를 알고 있다고 가정 할 수 있습니다) 우리가 try 문이 실패하지 않습니다 알 수 있도록하기 때문에 이러한 경우에 필요하다.

구문을 충족하고 컴파일 오류를 피하기 위해 끝에 추가 return 문을 넣는 것이 나쁜 습관입니까 (주석에 도달하지 않을 것임을 설명하는 주석 포함), 아니면 이와 같은 코드를 작성하는 더 좋은 방법이 있습니까? return 문이 필요하지 않습니까?



답변

추가 return 문이없는 더 명확한 방법은 다음과 같습니다. 나도 잡지 CloneNotSupportedException못하지만 발신자에게 전달합니다.

if (a != null) {
    try {
        return a.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
}
throw new TotallyFooException();

처음에 가지고있는 것보다 더 간단한 구문으로 끝나도록 순서를 조정하는 것이 거의 항상 가능합니다.


답변

확실히 도달 할 수 있습니다. catch절 에서 stacktrace 만 인쇄한다는 점에 유의하십시오 .

시나리오에서 a != null거기 것이다 예외가 될 수는는 return null 것이다 도달 할 수. 해당 문을 제거하고 throw new TotallyFooException();.

일반적으로 * , if null가 메서드 의 유효한 결과 (즉, 사용자가 예상 하고 의미하는 바가 있음) 인 경우 “데이터를 찾을 수 없음”또는 예외 발생에 대한 신호로 반환하는 것은 좋은 생각 이 아닙니다 . 그렇지 않으면 반환하지 않아야하는 이유를 알 수 없습니다 null.

예를 들어 Scanner#ioException방법을 살펴보십시오 .

IOException이 Scanner 기본이되는 Readable이 던진 마지막을 리턴합니다 . 이러한 예외가 없으면 이 메서드 null을 반환 합니다 .

이 경우 반환 된 값 null은 분명한 의미를 가지고 있습니다. 메소드를 사용할 때 메소드 null가 무언가를하려고했지만 실패했기 때문이 아니라 그러한 예외가 없었기 때문에 얻은 것임을 확신 할 수 있습니다 .

* 때로는 null의미가 모호한 경우에도 반환하고 싶을 때가 있습니다 . 예를 들어 HashMap#get:

반환 값이 null 이라고해서 반드시 맵에 키에 대한 매핑이 포함되어 있지 않음을 나타내는 것은 아닙니다. 지도가 명시 적으로 키를 null로 매핑 할 수도 있습니다 . containsKey작업은이 두 경우를 구별 할 수 있습니다.

이 경우 이 발견되어 반환되었거나 해시 맵에 요청 된 키가 포함되어 있지 않음을 null나타낼 수 있습니다 . null


답변

구문을 충족하고 컴파일 오류를 피하기 위해 끝에 추가 return 문을 넣는 것이 나쁜 습관입니까? (설명에 도달하지 않을 것임을 설명하는 주석 포함)

return null도달 할 수없는 분기의 종점에는 나쁜 습관 이라고 생각 합니다. 던져 더 RuntimeException( AssertionError그 라인 뭔가 아주 잘못 간에 도착하고 응용 프로그램이 알 수없는 상태에로서 또한 허용 될 것이다). 개발자가 무언가를 놓 쳤기 때문입니다 (객체는 null이 아니고 복제가 불가능할 수 있음).

VM보다 실수 할 가능성이 더 높기 때문에 InternalError코드에 도달 할 수 없는지 (예 : a 뒤에 System.exit()) 확실하지 않으면 사용 하지 않을 것입니다.

TotallyFooException“연결할 수없는 줄”에 도달하는 것이 해당 예외를 던지는 다른 곳과 동일한 의미 인 경우 에만 사용자 지정 예외 (예 :)를 사용합니다.


답변

당신 CloneNotSupportedException은 당신의 코드가 그것을 처리 할 수 ​​있다는 것을 의미합니다. 그러나 그것을 잡은 후에는 함수의 끝에 도달했을 때 무엇을해야할지 전혀 알지 못합니다. 이것은 그것을 처리 할 수 ​​없다는 것을 의미합니다. 따라서이 경우 코드 냄새라는 것이 맞으며 제 생각에는 CloneNotSupportedException.


답변

Objects.requireNonNull()매개 변수 a가 null이 아닌지 확인 하는 데 사용 하고 싶습니다 . 따라서 코드를 읽을 때 매개 변수가 null이 아니어야한다는 것이 분명합니다.

그리고 피할 검사 예외에 내가 던져 다시 것이다 CloneNotSupportedExceptionA와 RuntimeException.

둘 다 왜 이런 일이 일어나지 않아야하는지 또는 그렇지 않은지 의도와 함께 멋진 텍스트를 추가 할 수 있습니다.

public Object getClone(Object a) {

    Objects.requireNonNull(a);

    try {
        return a.clone();
    } catch (CloneNotSupportedException e) {
        throw new IllegalArgumentException(e);
    }

}


답변

이런 상황에서

public Object getClone(SomeInterface a) throws TotallyFooException {
    // Precondition: "a" should be null or should have a someMethod method that
    // does not throw a SomeException.
    if (a == null) {
        throw new TotallyFooException() ; }
    else {
        try {
            return a.someMethod(); }
        catch (SomeException e) {
            throw new IllegalArgumentException(e) ; } }
}

흥미롭게도 “try 문은 결코 실패하지 않을 것”이라고 말하지만, 여전히 e.printStackTrace();실행되지 않을 것이라고 주장하는 문을 작성하는 데 어려움을 겪었습니다 . 왜?

아마도 당신의 믿음이 그다지 확고하지 않은 것 같습니다. 당신의 믿음은 당신이 작성한 코드가 아니라 당신의 고객이 전제 조건을 위반하지 않을 것이라는 기대에 근거하기 때문에 (제 생각에는) 좋습니다. 공개 방법을 방어 적으로 프로그래밍하는 것이 좋습니다.

그건 그렇고, 당신의 코드는 나를 위해 컴파일되지 않을 것입니다. 당신은 호출 할 수 없습니다 a.clone()의 유형이 경우에도 a입니다 Cloneable. 적어도 Eclipse의 컴파일러는 그렇게 말합니다. 표현 a.clone()은 오류를 준다

clone () 메서드는 Cloneable 유형에 대해 정의되지 않았습니다.

당신의 특정한 경우에 제가 할 일은

public Object getClone(PubliclyCloneable a) throws TotallyFooException {
    if (a == null) {
        throw new TotallyFooException(); }
    else {
        return a.clone(); }
}

PubliclyCloneable의해 정의되는 곳

interface PubliclyCloneable {
    public Object clone() ;
}

또는 매개 변수 유형이이어야하는 Cloneable경우 최소한 다음이 컴파일됩니다.

public static Object getClone(Cloneable a) throws TotallyFooException {
//  Precondition: "a" should be null or point to an object that can be cloned without
// throwing any checked exception.
    if (a == null) {
        throw new TotallyFooException(); }
    else {
        try {
            return a.getClass().getMethod("clone").invoke(a) ; }
        catch( IllegalAccessException e ) {
            throw new AssertionError(null, e) ; }
        catch( InvocationTargetException e ) {
            Throwable t = e.getTargetException() ;
            if( t instanceof Error ) {
                // Unchecked exceptions are bubbled
                throw (Error) t ; }
            else if( t instanceof RuntimeException ) {
                // Unchecked exceptions are bubbled
                throw (RuntimeException) t ; }
            else {
                // Checked exceptions indicate a precondition violation.
                throw new IllegalArgumentException(t) ; } }
        catch( NoSuchMethodException e ) {
            throw new AssertionError(null, e) ; } }
}


답변

위의 예는 유효하고 매우 Java입니다. 그러나 다음은 해당 반환을 처리하는 방법에 대한 OP의 질문을 해결하는 방법입니다.

public Object getClone(Cloneable a) throws CloneNotSupportedException {
    return a.clone();
}

anull인지 확인 하는 데는 이점이 없습니다 . NPE로갑니다. 스택 추적을 인쇄하는 것도 도움이되지 않습니다. 스택 추적은 처리 위치에 관계없이 동일합니다.

도움이되지 않는 null 테스트와 도움이되지 않는 예외 처리로 코드를 정리하는 것은 이점이 없습니다. 쓰레기를 제거하면 반품 문제가 문제가됩니다.

(OP에는 예외 처리에 버그가 포함되어 있습니다. 이것이 반환이 필요한 이유입니다. OP는 제가 제안한 방법을 잘못하지 않았을 것입니다.)