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
두통을 줄일 수 있는 간단한 예입니다 .
public void setTest(String test) {
test = test;
}
setter에서 'this'키워드를 잊어 버린 경우 설정하려는 변수가 설정되지 않습니다. 그러나 final
매개 변수에 키워드 를 사용하면 컴파일 타임에 버그가 발생합니다.
변수 재 할당 중지
이 답변은 지적으로 흥미롭지 만 간단한 대답은 읽지 못했습니다.
컴파일러에서 변수가 다른 객체에 다시 할당되지 않도록하려면 final 키워드를 사용하십시오 .
변수가 정적 변수, 멤버 변수, 로컬 변수 또는 인수 / 매개 변수인지에 관계없이 효과는 완전히 동일합니다.
예
실제 효과를 봅시다.
두 개의 변수 ( arg 및 x )에 서로 다른 객체를 재 할당 할 수 있는이 간단한 방법을 고려하십시오 .
// 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 디자이너는 당시에 손이 가득했습니다.
완전성을 위해 추가 된 또 다른 사례
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
익명의 내부 클래스에서 변수를 사용하지 않는 한 개인적으로 로컬 변수 및 매개 변수 에 사용하지 않는 경향이 있지만 매개 변수 값 자체가 변경되지 않는다는 것을 분명히하려는 사람들의 요점을 확실히 알 수 있습니다 객체가 참조하는 경우 내용이 변경됨). 가독성을 높이는 사람들에게는 이것이 합리적이라고 생각합니다.
요점은 누군가가 실제로 주장한다면 더 중요 할 것 않았다 그러나 나는 그러한 주장을 본 기억이 없습니다 - 그것은하지 않는 방식으로 데이터를 일정하게 유지. 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
};
}
여기서 from
및 to
매개 변수는 최종 클래스 여야 익명 클래스 내부에서 사용할 수 있습니다.
해당 요구 사항의 이유는 다음과 같습니다. 로컬 변수는 스택에 존재하므로 메서드가 실행되는 동안에 만 존재합니다. 그러나 익명 클래스 인스턴스는 메소드에서 리턴되므로 훨씬 오래 지속될 수 있습니다. 후속 메소드 호출에 스택이 필요하므로 스택을 보존 할 수 없습니다.
따라서 Java가 대신하는 것은 로컬 변수의 복사본 을 숨겨진 인스턴스 변수로 익명 클래스에 넣는 것입니다 (바이트 코드를 검사하면 볼 수 있음). 그러나 최종 클래스가 아닌 경우 익명 클래스와 다른 클래스가 변수를 변경하는 방법을 볼 수 있습니다. 사본이 두 개가 아닌 변수가 하나만 있다는 착각을 유지하려면 최종 변수 여야합니다.
나는 매개 변수에 항상 final을 사용합니다.
그렇게 많이 추가합니까? 실제로는 아닙니다.
끄겠습니까? 아니.
이유 : 사람들이 느슨한 코드를 작성하고 접근 자에서 멤버 변수를 설정하지 못한 3 가지 버그가 발견되었습니다. 모든 버그를 찾기가 어려웠습니다.
향후 버전의 Java에서 이것이 기본값으로 사용되는 것을보고 싶습니다. 가치 / 참조에 의한 통과는 많은 주니어 프로그래머를 트립합니다.
한가지 더 .. 내 메소드는 매개 변수 수가 적어 메소드 선언의 추가 텍스트가 문제가되지 않습니다.
메소드 매개 변수에서 final을 사용하는 것은 호출자 측의 인수와 관련이 없습니다. 해당 메소드 내에서 변경되지 않는 것으로 표시하기위한 것입니다. 보다 기능적인 프로그래밍 스타일을 채택하려고 할 때 그 가치를 볼 수 있습니다.
개인적으로 나는 매개 변수 목록에 너무 많은 혼란을 더하기 때문에 메소드 매개 변수에 final을 사용하지 않습니다. Checkstyle과 같은 방법으로 메소드 매개 변수가 변경되지 않도록 강제하고 싶습니다.
지역 변수의 경우 가능할 때마다 final을 사용하고 개인 프로젝트 설정에서 Eclipse가 자동으로 수행하도록합니다.
C / C ++ const와 같은 강력한 것을 확실히 원합니다.
Java는 인수 사본을 전달하므로 관련성이 다소 제한적이라고 생각합니다. 나는 이것이 C ++ 시대에서 온 것으로 추측합니다. 여기에서를 변경하여 참조 내용이 변경되는 것을 막을 수 const char const *
있습니다. 나는 이런 종류의 것들이 개발자가 f ***처럼 내재 된 어리 석음을 믿고 자신이 타이핑하는 모든 캐릭터로부터 보호되어야한다고 생각합니다. 모든 겸손 함에서 나는 final
누군가가 내 방법과 물건을 재정의하지 않기를 원하지 않는 한 ...을 무시 하더라도 버그가 거의 없다고 말해야합니다 ... 어쩌면 나는 단지 구식 개발자 일 것입니다 : : -O
나는 매개 변수 목록에서 final을 사용하지 않으며 이전 응답자가 말한 것처럼 혼란을 더합니다. 또한 Eclipse에서 매개 변수 할당을 설정하여 오류를 생성 할 수 있으므로 매개 변수 목록에서 final을 사용하면 나에게 중복되는 것처럼 보입니다. 흥미롭게도 매개 변수 할당에 대한 Eclipse 설정을 활성화하면 오류가 발생 하여이 코드가 발생했습니다 (실제 코드가 아니라 흐름을 기억하는 방식입니다).
private String getString(String A, int i, String B, String C)
{
if (i > 0)
A += B;
if (i > 100)
A += C;
return A;
}
악마의 옹호자 플레이를하는 것, 이것이 정확히 무엇입니까?
짧은 대답 : final
약간 도움이되지만 ... 클라이언트 측에서 방어 프로그래밍을 대신 사용하십시오.
실제로 문제 final
는 참조 가 변경되지 않고 강제로 참조 된 객체 멤버가 호출자에게 알려지지 않은 상태로 변경되도록 강제한다는 것 입니다. 따라서 이와 관련하여 모범 사례는 호출자 측의 방어 프로그래밍으로, 변경이 불가능한 API 또는 엉뚱한 API로 인해 위험에 처할 수있는 매우 불변의 인스턴스 또는 딥 사본을 생성하는 것입니다.
매개 변수 선언에 final을 추가하는 또 다른 이유는 "추출 방법"리팩토링의 일부로 이름을 바꿔야하는 변수를 식별하는 데 도움이되기 때문입니다. 큰 메소드 리팩토링을 시작하기 전에 각 매개 변수에 final을 추가하면 계속하기 전에 해결해야 할 문제가 있는지 신속하게 알 수 있습니다.
그러나 리팩토링이 끝나면 일반적으로 불필요한 것으로 제거합니다.
Michel의 게시물이 후속 조치입니다. 나는 내 자신이 그것을 설명하는 또 다른 예를 만들었다. 도움이 되길 바랍니다.
public static void main(String[] args){
MyParam myParam = thisIsWhy(new MyObj());
myParam.setArgNewName();
System.out.println(myParam.showObjName());
}
public static MyParam thisIsWhy(final MyObj obj){
MyParam myParam = new MyParam() {
@Override
public void setArgNewName() {
obj.name = "afterSet";
}
@Override
public String showObjName(){
return obj.name;
}
};
return myParam;
}
public static class MyObj{
String name = "beforeSet";
public MyObj() {
}
}
public abstract static class MyParam{
public abstract void setArgNewName();
public abstract String showObjName();
}
메소드에서 위의 코드에서 thisIsWhy () , 우리가 실제로 할당하지 않은 [인수하여 MyObj OBJ] A와 실제 참조 하여 myParam에 있습니다. 대신 MyParam 내부의 메소드에서 [argument MyObj obj] 를 사용합니다.
그러나 우리는 방법 마친 후 thisIsWhy ()를 , 인수가 (객체)하여 MyObj은 여전히 존재한다?
main에서 볼 수 있기 때문에 showObjName () 메소드를 호출하고 obj 에 도달해야하기 때문에 있어야합니다 . MyParam은 메소드가 이미 리턴 된 경우에도 메소드 인수를 사용 / 도달합니다!
Java가 실제로 사본을 생성하는 방법 은 MyParam 객체 내부의 MyObj obj 인수에 대한 숨겨진 참조입니다 (그러나 MyParam의 공식 필드가 아니므로 볼 수 없습니다)
"showObjName"을 호출 할 때 해당 참조를 사용하여 해당 값을 가져옵니다.
그러나 인수를 final로 지정하지 않으면 상황이 발생하여 MyObj obj 인수에 새 메모리 (객체)를 다시 할당 할 수 있습니다 .
기술적으로 전혀 충돌이 없습니다! 우리가 그렇게 할 수 있다면 다음과 같은 상황이 있습니다.
- 이제 숨겨진 [MyObj obj]가 MyParam 객체에있는 [Memory A in heap]을 가리 킵니다.
- 또 다른 [MyObj obj]가 있는데, 이것은 [IsWhy] 메소드에 살고있는 [Memory B in heap]을 가리키는 인수입니다.
충돌은 없지만 "혼란 !!" 이들은 모두 "obj"인 동일한 "참조 이름"을 사용하기 때문 입니다.
이를 피하려면 프로그래머가 "실수하기 쉬운"코드를 수행하지 않도록 "최종"으로 설정하십시오.
'Programing' 카테고리의 다른 글
요소를 제거하지 않고 세트에서 요소를 검색하는 방법은 무엇입니까? (0) | 2020.02.28 |
---|---|
Google은 어떤 데이터베이스를 사용합니까? (0) | 2020.02.28 |
참조 및 값별 자바 스크립트 (0) | 2020.02.28 |
Android의 PreferenceActivity에서 SharedPreference를 가져 오려면 어떻게합니까? (0) | 2020.02.28 |
NSOrderedSet 생성 접근 자에서 예외가 발생했습니다. (0) | 2020.02.28 |