메소드 매개변수와 지역 변수에 언제 final을 사용해야 합니까?

StackOverflow https://stackoverflow.com/questions/154314

  •  03-07-2019
  •  | 
  •  

문제

나는 몇 가지 참고 자료를 찾았습니다 (예를 들어) 사용을 제안하는 final 가능한 한 많이 그리고 그것이 얼마나 중요한지 궁금합니다.이는 주로 최종 메소드나 클래스가 아닌 메소드 매개변수 및 지역 변수의 맥락에서 발생합니다.상수의 경우 이는 분명 의미가 있습니다.

한편으로 컴파일러는 일부 최적화를 수행할 수 있으며 이를 통해 프로그래머의 의도가 더욱 명확해집니다.반면에, 자세한 내용을 추가하고 최적화가 사소할 수 있습니다.

기억하기 위해 노력해야 하는 부분인가요?

도움이 되었습니까?

해결책

집착 :

  • 최종 필드 - 필드를 최종으로 마킹하면 건축 종료로 설정되도록하여 필드 참조가 불변을 불러 일으 킵니다. 이를 통해 필드를 안전하게 발행 할 수 있으며 나중에 읽는 것의 동기화가 필요하지 않습니다. (객체 참조의 경우 필드 참조 만 불변입니다. 객체 참조가 여전히 변경 될 수 있고 불변성에 영향을 줄 수 있습니다.)
  • 최종 정적 필드 - 정적 최종 필드를 사용했던 많은 경우에 열거를 사용하지만 지금은 열거를 사용합니다.

신중하게 사용하십시오.

  • 최종 클래스 - 프레임 워크/API 디자인만이 내가 고려한 유일한 사례입니다.
  • 최종 방법 - 기본적으로 최종 클래스와 동일합니다. Crazy 및 Marking Stuff Final과 같은 템플릿 메소드 패턴을 사용하는 경우 상속에 너무 의존하고 대표단에는 충분하지 않을 수 있습니다.

항문을 느끼지 않는 한 무시하십시오.

  • 메소드 매개 변수와 로컬 변수 - 나는 게으르고 코드가 막히기 때문에 거의이 작업을 거의하지 않습니다. 나는 수정하지 않을 매개 변수와 로컬 변수를 표시하는 것을 완전히 인정할 것입니다. 나는 그것이 기본이 되었으면 좋겠다. 그러나 그것은 그렇지 않으며 결승전에서 코드를 이해하기가 더 어렵다는 것을 알았습니다. 내가 다른 사람의 코드에 있다면, 나는 그것들을 꺼내지 않을 것이지만 새 코드를 작성하는 경우에 넣지 않을 것입니다. 한 가지 예외는 당신이 최종적으로 무언가를 표시 해야하는 경우에 액세스 할 수있는 경우입니다. 익명의 내부 수업 내에서.

다른 팁

기억하기 위해 노력해야 할 일입니까?

아니요, Eclipse를 사용하는 경우 자동으로 추가하기 위해 저장 작업을 구성 할 수 있기 때문에 결정적인 당신을위한 수정 자. 그런 다음 더 적은 노력으로 혜택을 얻습니다.

"최종"의 개발 시간 이점은 최소한 런타임 이점만큼 중요합니다.이는 향후 코드 편집자에게 귀하의 의도에 대해 알려줍니다.

클래스를 "최종"으로 표시하는 것은 클래스를 디자인하거나 구현하는 동안 확장을 정상적으로 처리하기 위해 노력하지 않았음을 나타냅니다.독자가 클래스를 변경할 수 있고 "최종" 수정자를 제거하려는 경우 위험은 스스로 감수할 수 있습니다.클래스가 확장을 잘 처리하는지 확인하는 것은 그들에게 달려 있습니다.

변수를 "final"로 표시하고 생성자에 할당하는 것은 종속성 주입에 유용합니다.이는 변수의 "협력자" 특성을 나타냅니다.

메소드를 "final"로 표시하는 것은 추상 클래스에서 유용합니다.확장 지점이 어디에 있는지 명확하게 설명합니다.

마킹 방법 매개 변수와 현지인을 찾았습니다 final 해당 메소드가 여러 페이지 길이의 이해가 불가능한 혼란 일 때 리팩토링 보조금으로 유용합니다. 소량 final 자유롭게, "최종 변수에 할당 할 수 없음"오류가 컴파일러 (또는 IDE)가 던지는 오류를 보면, "데이터"라는 변수가 몇몇 (구식) 댓글이 맹세하더라도 널이 끝나는 이유를 발견 할 수 있습니다. T가 발생합니다.

그런 다음 재사용 된 변수를 사용 지점에 더 가깝게 선언 된 새로운 변수로 교체하여 일부 오류를 수정할 수 있습니다. 그런 다음 스코핑 브레이스에서 방법의 전체 부분을 감싸고 갑자기 "추출 방법"에서 멀리 떨어진 IDE 키 프레스가 더 이해하기 쉬워졌습니다.

당신의 방법이 있다면 ~ 아니다 이미 인재 할 수없는 난파선, 사람들이 해당 난파선으로 바꾸는 것을 방해하는 물건을 최종적으로 만드는 데 가치가있을 것 같아요. 그러나 짧은 방법이라면 (참조 할 수 없음) 많은 구두를 추가 할 위험이 있습니다. 특히, Java 기능 서명은 인수 당 6 개를 더 추가하지 않기 때문에 80 자로 맞을 정도로 어렵습니다!

나는 사용한다 final Java를 더 많은 표현을 기반으로 만들기 위해 항상. Java의 조건을 참조하십시오 (if,else,switch)는 기능적 프로그래밍 (예 : ML, Scala 또는 LISP)에 사용 된 경우 항상 싫어하는 표현 기반이 아닙니다.

따라서 조건을 사용할 때는 항상 최종 변수를 사용하려고 노력해야합니다.

예를 들어 보겠습니다.

    final String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            break;
        case JOB_POSTING_IMPORT:
            name = "Blah";
            break;
        default:
            throw new IllegalStateException();
    }

이제 다른 것을 추가하면 case 진술과 설정하지 않습니다 name 컴파일러가 실패합니다. 모든 케이스에서 깨지지 않으면 컴파일러도 실패합니다 (변수를 설정 함). 이를 통해 Java를 LISP와 매우 유사하게 만들 수 있습니다. let 표현식과 코드가 거해 범위 변수로 인해 크게 들여 쓰기되지 않도록합니다.

그리고 @recurse가 언급했듯이 (그러나 분명히 -1 me) 당신은 앞의를 만들 수 있습니다. String name final 컴파일러 오류를 얻으려면 (내가 말할 수 없다고 말한 적이 없음) 스위치 명령문이 나온 후 컴파일러 오류를 쉽게 제거 할 수 있습니다. break 사용하지 않고 오류를 일으킬 수없는 (@recurse가 말하는 것에도 불구하고) final:

    String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            //break; whoops forgot break.. 
            //this will cause a compile error for final ;P @Recurse
        case JOB_POSTING_IMPORT:
            name = "Blah";
            break;
    }
    // code, code, code
    // Below is not possible with final
    name = "Whoops bug";

버그 설정 이름 때문에 (잊어 버리는 것 외에 break 또 다른 버그) 이제 실수로 이것을 할 수 있습니다.

    String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            break;
        //should have handled all the cases for pluginType
    }
    // code, code, code
    // Below is not possible with final
    name = "Whoops bug";

최종 변수는 이름이 무엇인지에 대한 단일 평가를 강요합니다. 리턴 값이있는 함수가 항상 값을 반환 해야하는 방식과 유사하게 (예외 무시) 이름 스위치 블록은 이름을 해결해야하므로 해당 스위치 블록에 바인딩되어 코드의 리팩토링 청크를 더 쉽게 만들 수 있습니다 (예 : Eclipe Refactor : Extract Method). .

위의 OCAML :

type plugin = CandidateExport | JobPostingImport

let p = CandidateExport

let name = match p with
    | CandidateExport -> "Candidate Stuff"
    | JobPostingImport -> "Blah" ;;

그만큼 match ... with ... 함수 IE 표현식처럼 평가합니다. 스위치 문처럼 어떻게 보이는지 주목하십시오.

다음은 계획 (라켓 또는 닭고기)의 예입니다.

(define name 
    (match b
      ['CandidateExport "Candidate Stuff"]
      ['JobPostingImport "Blah"]))

글쎄, 이것은 모두 당신의 스타일에 달려 있습니다 ... 당신이 변수를 수정하지 않을 때 결승전을보고 싶다면 그것을 사용하십시오. 당신이 그것을 보는 것을 좋아하지 않는다면 ... 그럼 그대로 꺼내십시오.

나는 개인적으로 가능한 한 적은 수의 진실성을 좋아하므로 실제로는 필요하지 않은 추가 키워드를 사용하지 않는 경향이 있습니다.

나는 역동적 인 언어를 선호하기 때문에 아마도 진실성을 피하는 것을 좋아하는 것은 놀라운 일이 아닙니다.

그래서 나는 당신이 기대하는 방향을 선택하고 그냥 함께 가고 있다고 말할 것입니다 (케이스가 무엇이든 일관성을 유지하려고 노력하십시오).


부수적으로, 나는 그러한 패턴을 사용하고 사용하지 않는 프로젝트를 수행했으며, 버그 나 오류의 양에 차이가 없었습니다 ... 나는 그것이 큰 패턴이라고 생각하지 않습니다. 버그 수를 향상 시키지만, 다시 스타일이며, 수정하지 않을 의도를 표현하는 것을 좋아한다면 계속해서 사용하십시오.

우연히 매개 변수 값을 변경하지 않고 미묘한 버그를 도입하는 것은 매개 변수에서 유용합니다. 나는이 권장 사항을 무시하는 데 사용하지만 약 4 시간을 소비 한 후에는 사용합니다. 끔찍한 방법으로 (수백 줄의 코드와 여러 개의 포트, 중첩 된 IFS 및 모든 종류의 나쁜 관행이 포함되어 있음), 나는 당신이 그것을하는 것이 좋습니다.

 public int processSomethingCritical( final int x, final int y ){
 // hundreds of lines here 
     // for loop here...
         int x2 = 0;
        x++; // bug aarrgg...
 // hundreds of lines there
 // if( x == 0 ) { ...

 }

물론 완벽한 세상에서는 이런 일이 일어나지 않을 것이지만 .. 글쎄 .. 때로는 다른 사람들 코드를 지원해야합니다. :(

1 년 후에 누군가가 코드를 읽어야하는 응용 프로그램을 작성하는 경우, 예, 항상 수정해서는 안되는 변수에 대한 최종을 사용하십시오. 이렇게하면 코드가 더 "자체 문서화"가 될 것이며 다른 개발자가 로컬 상수를 로컬 임시 변수로 사용하는 것과 같은 어리석은 일을 할 가능성도 줄어 듭니다.

당신이 약간의 던지기 코드를 쓰고 있다면, 아니, 아니, 모든 상수를 식별하고 그것을 최종적으로 만들지 마십시오.

최대한 최종적으로 사용하겠습니다. 의도하지 않게 필드를 바꾸면 그렇게하면 플래그가 줄어 듭니다. 또한 메소드 매개 변수를 최종으로 설정했습니다. 그렇게하면서 나는 그들이 값으로 Java를 통과하는 것을 잊어 버린 매개 변수를 '설정'할 때 인수 한 코드에서 몇 가지 버그를 잡았습니다.

이것이 명백한 지에 대한 질문에서 명확하지 않지만 메소드 매개 변수를 최종적으로 만드는 것은 메소드의 본문에만 영향을 미칩니다. 그렇습니다 아니다 이 방법의 의도에 대한 흥미로운 정보를 Invoker에 전달하십시오. 통과되는 물체는 여전히 방법 내에서 돌연변이 될 수 있으며 (결승은 구성이 아님) 변수의 범위는 방법 내에 있습니다.

정확한 질문에 답하기 위해 코드가 요구하지 않는 한 (예 : 변수는 내부 클래스에서 참조되는) 인스턴스 또는 로컬 변수 (메소드 매개 변수 포함) 최종을 귀찮게하지 않습니다.

예를 들어 변수의 경우 논리적으로 상수라면 최종적으로 만듭니다.

변수에는 많은 용도가 있습니다 final. 여기 몇 가지가 있습니다

최종 상수

 public static class CircleToolsBetter {
     public final static double PI = 3.141;
        public double getCircleArea(final double radius) {
          return (Math.pow(radius, 2) * PI);
        }
    }

이것은 코드의 다른 부분에 사용할 수 있거나 다른 클래스에서 액세스 할 수 있습니다. 그렇다면 값을 하나씩 변경할 필요가 없다면 하나씩 변경할 필요가 없습니다.

최종 변수

public static String someMethod(final String environmentKey) {
    final String key = "env." + environmentKey;
    System.out.println("Key is: " + key);
    return (System.getProperty(key));

  }

}

이 클래스에서는 매개 변수 환경 키에 접두사를 추가하는 범위의 최종 변수를 구축합니다. 이 경우 최종 변수는 실행 범위 내에서만 최종적이며 메소드 실행마다 다릅니다. 메소드가 입력 될 때마다 결승전이 재구성됩니다. 구성 되 자마자 메소드 실행 범위에서 변경할 수 없습니다. 이를 통해 메소드 기간 동안 메소드에서 변수를 수정할 수 있습니다. 아래를 참조하십시오 :

public class FinalVariables {


  public final static void main(final String[] args) {
    System.out.println("Note how the key variable is changed.");
    someMethod("JAVA_HOME");
    someMethod("ANT_HOME");
  }
}

최종 상수

public double equation2Better(final double inputValue) {
    final double K = 1.414;
    final double X = 45.0;

double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M);
double powInputValue = 0;         
if (result > 360) {
  powInputValue = X * Math.sin(result); 
} else {
  inputValue = K * Math.sin(result);   // <= Compiler error   
}

코드가 실제로 긴 경우에 특히 유용하며 컴파일러 오류가 발생하여 누군가가 실수로 변경하지 말아야 할 변수를 변경할 때 로직/비즈니스 오류로 실행되지 않습니다.

최종 컬렉션

우리가 컬렉션에 대해 이야기 할 때 다른 경우, 당신은 그것들을 수정할 수없는 것으로 설정해야합니다.

 public final static Set VALID_COLORS; 
    static {
      Set temp = new HashSet( );
      temp.add(Color.red);
      temp.add(Color.orange);
      temp.add(Color.yellow);
      temp.add(Color.green);
      temp.add(Color.blue);
      temp.add(Color.decode("#4B0082")); // indigo
      temp.add(Color.decode("#8A2BE2")); // violet
      VALID_COLORS = Collections.unmodifiableSet(temp);
    }

그렇지 않으면, 당신이 그것을 선택할 수없는 것으로 설정하지 않으면 :

Set colors = Rainbow.VALID_COLORS;
colors.add(Color.black); // <= logic error but allowed by compiler

최종 수업 그리고 최종 방법 각각 확장 또는 덮어 쓰지 못합니다.

편집 : 캡슐화와 관련된 최종 클래스 문제를 해결하려면 :

수업을 결승시키는 방법에는 두 가지가 있습니다. 첫 번째는 클래스 선언에서 키워드 결승을 사용하는 것입니다.

public final class SomeClass {
  //  . . . Class contents
}

수업을 결승하게 만드는 두 번째 방법은 모든 생성자를 개인으로 선언하는 것입니다.

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
  private SomeClass(final int value) {
  }

최종적으로 표시하면이 테스트 클래스를 살펴 보는 것이 실제 최종임을 알면 문제가 절약됩니다. 언뜻보기에 공개적으로 보입니다.

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

불행히도, 클래스의 유일한 생성자는 비공개 이므로이 클래스를 연장하는 것은 불가능합니다. 테스트 클래스의 경우 클래스가 최종적이어야 할 이유가 없습니다. 테스트 클래스는 암시 적 최종 클래스가 어떻게 문제를 일으킬 수 있는지에 대한 좋은 예입니다.

따라서 생성자를 비공개로 만들어 클래스를 암시 적으로 최종적으로 만들 때 최종적으로 표시해야합니다.

당신이 언급 한 바와 같이 약간의 상충 관계이지만, 나는 암시 적 사용 이상의 것을 명시 적으로 사용하는 것을 선호합니다. 이렇게하면 미래의 코드 유지 관리자에 대한 모호성을 제거하는 데 도움이됩니다.

내부 (익명) 클래스가 있고 방법이 포함 된 방법의 변수에 액세스 해야하는 경우 해당 변수를 최종적으로해야합니다.

그 외에는 당신이 말한 것이 옳습니다.

사용 final 해당 변수를 다음과 같이 만드는 경우 변수에 대한 키워드 immutable

변수를 최종 변수로 선언함으로써 개발자는 다중 스레드 환경에서 발생할 수 있는 변수 수정 문제를 배제하는 데 도움이 됩니다.

Java 8 릴리스에는 "effectively final variable". 최종 변수가 아닌 변수는 최종 변수로 이동할 수 있습니다.

람다 식에서 참조되는 지역 변수는 최종이거나 사실상 최종이어야 합니다.

변수가 고려됩니다 효과적인 최종 로컬 블록에서 초기화 후 수정되지 않은 경우.즉, 사실상 final이어야 하는 경우 익명 클래스나 람다 표현식 내에서 final 키워드 없이 지역 변수를 사용할 수 있다는 의미입니다.

Java 7까지는 익명 클래스 내에서 최종이 아닌 지역 변수를 사용할 수 없지만 Java 8에서는 다음을 수행할 수 있습니다.

이것 좀 보세요 기사

매우 간단한 대답은 변수가있는 최종 3 건, Methods && Final with Classe ..

1. 변수와 관련하여 :이 변수를 한 번 이상 할당 할 수 없습니다 ..

2. 방법과 관련하여 :이 방법을 무시할 수 없습니다 ..

3. 수업과의 기본 : U 최종 수업을 연장 할 수 없습니다.

우선, 최종 키워드는 변수를 일정하게 만드는 데 사용됩니다. 일정하다는 것은 그것이 변하지 않는다는 것을 의미합니다. 예를 들어:

final int CM_PER_INCH = 2.54;

인치당 센티미터가 변하지 않기 때문에 변수 최종을 선언합니다.

최종 값을 무시하려고하면 변수가 먼저 선언 된 변수입니다. 예를 들어:

final String helloworld = "Hello World";
helloworld = "A String"; //helloworld still equals "Hello World"

다음과 같은 컴파일 오류가 있습니다.

local variable is accessed from inner class, must be declared final

변수가 최종적으로 선언 될 수 없거나 최종적으로 선언하고 싶지 않은 경우 다음을 시도하십시오.

final String[] helloworld = new String[1];
helloworld[0] = "Hello World!";
System.out.println(helloworld[0]);
helloworld[0] = "A String";
System.out.println(helloworld[0]);

이것은 인쇄 할 것입니다 :

Hello World!
A String
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top