[java] Java의 메소드 매개 변수에 키워드 “final”을 사용해야하는 이유는 무엇입니까?

메소드 매개 변수에 사용될 때 final키워드가 실제로 편리한 위치를 이해할 수 없습니다 .

익명 클래스의 사용, 가독성 및 의도 선언을 제외하면 거의 가치가 없어 보입니다.

일부 데이터를 일정하게 유지하는 것은 생각만큼 강력하지 않습니다.

  • 매개 변수가 프리미티브 인 경우 매개 변수가 메소드에 값으로 전달되므로 변경되지 않고 범위를 벗어나지 않습니다.

  • 참조로 매개 변수를 전달하는 경우 참조 자체는 로컬 변수이며 참조가 메소드 내에서 변경되면 메소드 범위 외부에서 영향을 미치지 않습니다.

아래의 간단한 테스트 예제를 고려하십시오. 이 테스트는 메소드가 주어진 참조의 값을 변경했지만 효과가 없습니다.

public void testNullify() {
    Collection<Integer> c  = new ArrayList<Integer>();      
    nullify(c);
    assertNotNull(c);       
    final Collection<Integer> c1 = c;
    assertTrue(c1.equals(c));
    change(c);
    assertTrue(c1.equals(c));
}

private void change(Collection<Integer> c) {
    c = new ArrayList<Integer>();
}

public void nullify(Collection<?> t) {
    t = null;
}



답변

변수 재 할당 중지

이 답변은 지적으로 흥미롭지 만 간단한 대답은 읽지 못했습니다.

컴파일러에서 변수가 다른 객체에 다시 할당되지 않도록하려면 final 키워드를 사용하십시오 .

변수가 정적 변수, 멤버 변수, 로컬 변수 또는 인수 / 매개 변수인지에 관계없이 효과는 완전히 동일합니다.

실제로 효과를 보자.

두 개의 변수 ( argx )에 서로 다른 객체를 재 할당 할 수 있는이 간단한 방법을 고려하십시오 .

// Example use of this method: 
//   this.doSomething( "tiger" );
void doSomething( String arg ) {
  String x = arg;   // Both variables now point to the same String object.
  x = "elephant";   // This variable now points to a different String object.
  arg = "giraffe";  // Ditto. Now neither variable points to the original passed String.
}

지역 변수를 final 로 표시하십시오 . 컴파일러 오류가 발생합니다.

void doSomething( String arg ) {
  final String x = arg;  // Mark variable as 'final'.
  x = "elephant";  // Compiler error: The final local variable x cannot be assigned. 
  arg = "giraffe";  
}

대신 매개 변수를 final 로 표시해 봅시다 . 컴파일러 오류가 발생합니다.

void doSomething( final String arg ) {  // Mark argument as 'final'.
  String x = arg;   
  x = "elephant"; 
  arg = "giraffe";  // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}

이야기의 교훈:

변수가 항상 같은 객체를 가리 키도록하려면 변수를 final로 표시하십시오 .

인수를 다시 할당하지 마십시오

좋은 프로그래밍 방법 (모든 언어로)으로, 호출 메소드가 전달한 오브젝트 이외의 오브젝트에 매개 변수 / 인수 변수를 다시 지정 해서는 안됩니다 . 위의 예에서 절대로 줄을 쓰지 않아야합니다 arg =. 인간은 실수를 저지르고 프로그래머는 인간이므로 컴파일러에게 도움을 요청하십시오. 컴파일러가 이러한 재 할당을 찾아 플래그를 지정할 수 있도록 모든 매개 변수 / 인수 변수를 ‘final’로 표시하십시오.

회고

다른 답변에서 언급했듯이 … 프로그래머가 배열의 끝을지나 읽는 것과 같은 멍청한 실수를 피하도록 돕는 Java의 원래 설계 목표를 감안할 때 Java는 모든 매개 변수 / 인수 변수를 ‘최종’으로 자동 시행하도록 설계되어야합니다. 즉, 인수는 변수가 아니어야합니다 . 그러나 뒤늦은 시각은 20/20 비전이며 Java 디자이너는 당시 손을 가득 채웠습니다.

따라서 항상 final모든 인수에 추가 합니까?

final선언되는 각각의 모든 메소드 매개 변수에 추가해야합니까 ?

  • 이론적으로는 그렇습니다.
  • 실제로는 아닙니다. method 메소드의 코드가 길거나 복잡한 경우에만
    추가하십시오 final. 인수는 로컬 또는 멤버 변수로 오인되어 재 할당 될 수 있습니다.

인수를 다시 할당하지 않는 관행을 사면 각각에 인수를 추가하는 경향이 있습니다 final. 그러나 이것은 지루하며 선언을 조금 더 읽기 어렵게 만듭니다.

인수가 지역 변수 또는 멤버 변수가 아닌 인수 인 짧은 간단한 코드의 경우에는을 추가하지 않아도 final됩니다. 코드가 분명하고 나나 다른 프로그래머가 실수로 인수 변수를 인수가 아닌 다른 인수로 잘못 리팩토링하거나 리팩토링 할 가능성이 없다면 귀찮게하지 마십시오. 내 자신의 작업에서 final인수가 로컬 또는 멤버 변수로 착각 할 수있는 더 길거나 관련된 코드 만 추가 합니다.

완전성을 위해 추가 된 또 다른 사례

public class MyClass {
    private int x;
    //getters and setters
}

void doSomething( final MyClass arg ) {  // Mark argument as 'final'.

   arg =  new MyClass();  // Compiler error: The passed argument variable arg  cannot be re-assigned to another object.

   arg.setX(20); // allowed
  // We can re-assign properties of argument which is marked as final
 }


답변

때로는 변수가 변경되지 않는다는 것을 명확하게 (가독성을 위해) 밝히는 것이 좋습니다. 다음은 사용으로 final두통을 줄일 수 있는 간단한 예입니다 .

public void setTest(String test) {
    test = test;
}

setter에서 ‘this’키워드를 잊어 버린 경우 설정하려는 변수가 설정되지 않습니다. 그러나 final매개 변수에 키워드 를 사용하면 컴파일 타임에 버그가 발생합니다.


답변

예, 익명 클래스, 가독성 및 의도 선언을 제외하면 거의 가치가 없습니다. 그래도 그 세 가지는 가치가 없습니까?

final익명의 내부 클래스에서 변수를 사용하지 않는 한 개인적으로 로컬 변수 및 매개 변수 에 사용하지 않는 경향이 있지만 매개 변수 값 자체가 변경되지 않는다는 것을 분명히하려는 사람들의 요점을 확실히 알 수 있습니다 객체가 참조하는 경우 내용이 변경됨). 가독성을 높이는 사람들에게는 이것이 합리적이라고 생각합니다.

요점은 누군가가 실제로 주장한다면 더 중요 할 것 않았다 그러나 나는 그러한 주장을 본 기억이 없습니다 – 그것은하지 않는 방식으로 데이터를 일정하게 유지. final실제보다 더 많은 효과가 있다고 제안하는 많은 개발자가 있다고 제안하고 있습니까?

편집 : 나는이 모든 것을 Monty Python 참조와 요약해야합니다. 이 질문은 “로마인들이 우리를 위해 무엇을 해왔습니까?”


답변

나에게 당신은 하나의 사건에 대해 조금 설명하자 존 이미 언급 한 최종 사용을 :

메소드에서 익명의 내부 클래스를 작성하고 해당 클래스 내에 로컬 변수 (예 : 메소드 매개 변수)를 사용하는 경우 컴파일러는 매개 변수를 final로 설정하도록합니다.

public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
    return new Iterator<Integer>(){
        int index = from;
        public Integer next()
        {
            return index++;
        }
        public boolean hasNext()
        {
            return index <= to;
        }
        // remove method omitted
    };
}

여기서 fromto매개 변수는 최종 클래스 여야 익명 클래스 내부에서 사용할 수 있습니다.

해당 요구 사항의 이유는 다음과 같습니다. 로컬 변수는 스택에 존재하므로 메서드가 실행되는 동안에 만 존재합니다. 그러나 익명 클래스 인스턴스는 메소드에서 리턴되므로 훨씬 오래 지속될 수 있습니다. 후속 메소드 호출에 스택이 필요하므로 스택을 보존 할 수 없습니다.

Java가 대신 로컬 변수의 사본 을 숨겨진 인스턴스 변수로 익명 클래스에 넣는 것입니다 (바이트 코드를 검사하면 볼 수 있음). 그러나 최종 클래스가 아닌 경우 익명 클래스와 다른 클래스가 변수를 변경하는 메소드를 볼 수 있습니다. 사본이 두 개가 아닌 변수가 하나만 있다는 착각을 유지하려면 최종 변수 여야합니다.


답변

나는 매개 변수에 항상 final을 사용합니다.

그렇게 많이 추가합니까? 실제로는 아닙니다.

끄겠습니까? 아니.

이유 : 사람들이 느슨한 코드를 작성하고 접근 자에서 멤버 변수를 설정하지 못한 3 가지 버그를 발견했습니다. 모든 버그를 찾기가 어려웠습니다.

향후 버전의 Java에서 이것이 기본값으로 사용되기를 바랍니다. 가치 / 참조에 의한 통과는 많은 주니어 프로그래머를 트립합니다.

한가지 더. 내 메소드에는 매개 변수 수가 적어 메소드 선언에 대한 추가 텍스트가 문제가되지 않습니다.


답변

메소드 매개 변수에서 final을 사용하는 것은 호출자 측의 인수와 관련이 없습니다. 해당 메소드 내에서 변경되지 않는 것으로 표시하기위한 것입니다. 보다 기능적인 프로그래밍 스타일을 채택하려고 할 때 그 가치를 볼 수 있습니다.


답변

개인적으로 나는 매개 변수 목록에 너무 많은 혼란을 더하기 때문에 메소드 매개 변수에 final을 사용하지 않습니다. Checkstyle과 같은 방법으로 메소드 매개 변수가 변경되지 않도록 강제하고 싶습니다.

로컬 변수의 경우 가능할 때마다 final을 사용하고 개인 프로젝트 설정에서 Eclipse가 자동으로 수행하도록합니다.

C / C ++ const와 같은 강력한 것을 확실히 원합니다.