리플렉션을 사용하여 일반 세터와 게터를 만드는 것이 좋지 않은 이유는 무엇입니까?


49

얼마 전 나는 모든 가변 변수에 대해 getter와 setter를 피하는 방법에 대한 질문 에이 답변 을 썼습니다 . 당시 나는 이것이 나쁜 생각이라고 말로 표현하기 어려웠지만 OP는 그것을 어떻게 해야하는지 명시 적으로 요구했다. 나는 이것이 왜 문제가 될 수 있는지에 대해 여기에서 검색했고, 이 질문을 찾았습니다. 이 질문 은 절대적으로 필요한 경우가 아니라면 리플렉션을 사용하는 것이 나쁜 습관이라고 생각합니다.

그래서 누군가가 왜 일반적인 게터와 세터의 경우에 반성을 피해야하는지 왜 구두로 말할 수 있습니까?


12
보일러 플레이트를 줄이려면 Lombok과 Groovy가 조용하지만 안전하게 게터와 세터를 생성합니다.
chrylis

2
Java와 같은 정적 언어로 이러한 게터와 세터를 어떻게 소비합니까? 나에게는 스타터가 아닌 것 같습니다.
Esben Skov Pedersen

@EsbenSkovPedersen 당신은 많이처럼 그들을 소비하는 것 Map'들 getput일을 꽤 투박한 방법입니다.
HAEM

2
IIRC, Lombok은 적절한 주석에 따라 컴파일 타임에 게터 / 세터를 합성하므로 리플렉션의 성능 저하가 발생하지 않고 정적으로 분석 / 인라인 될 수 있으며 리팩터링 될 수 있습니다 (IntelliJ에는 Lombok 용 플러그인이 있음)
Alexander

답변:


117

일반적으로 반사의 단점

리플렉션은 직선 코드보다 이해하기 어렵습니다.

내 경험상 리플렉션은 Java의 "전문가 수준"기능입니다. 나는 대부분의 프로그래머들이 리플렉션을 적극적으로 사용하지 않는다고 주장한다 (즉 리플렉션을 사용하는 라이브러리를 소비하는 것은 중요하지 않다). 따라서 프로그래머가 코드를 이해하기 어렵습니다.

정적 분석에는 반사 코드에 액세스 할 수 없습니다

getFoo클래스에 게터 가 있고 이름을로 바꾸고 싶다고 가정 합니다 getBar. 리플렉션을 사용하지 않으면 코드베이스를 검색하고 getFoogetter를 사용하는 모든 장소를 찾을 수 있으므로 업데이트 할 수 있으며 누락 된 경우에도 컴파일러가 불평합니다.

그러나 getter를 사용하는 장소가 callGetter("Foo")and callGetterdoes getClass().getMethod("get"+name).invoke(this)인 경우 위의 방법으로 찾지 못하고 컴파일러는 불평하지 않습니다. 코드가 실제로 실행될 때만을 얻을 수 있습니다 NoSuchMethodException. 그리고 그 예외 (추적 된)가 callGetter"하드 코딩 된 문자열에만 사용되며 실제로는 일어날 수 없기 때문에" 삼켜 질 때의 고통을 상상해보십시오 . OP가 자신의 SO 답변에서 정확히 그렇게 한 것을 제외하고 는 필드 이름이 변경되면 일반 세터 사용자는 아무런 조치도 취하지 않는 세터의 매우 모호한 버그를 제외하고는 알 수 없습니다. 운이 좋으면 getter 사용자는 무시 된 예외의 콘솔 출력을 볼 수 있습니다.)

컴파일러가 리플렉션 코드를 타입 검사하지 않음

이것은 기본적으로 위의 큰 하위 지점입니다. 리플렉션 코드는 모든 것 Object입니다. 런타임에 유형을 확인합니다. 오류는 단위 테스트에서 발견되지만 적용 범위가있는 경우에만 발견됩니다. ( "이것은 단지 getter 일 뿐이다. 테스트 할 필요는 없다")

리플렉션 코드를 최적화에 사용할 수 없습니다

이론적으로는 아니지만 실제로는 인라인 캐시를 인라인하거나 생성하는 JVM을 찾을 수 없습니다 Method.invoke. 이러한 최적화를 위해 일반적인 메소드 호출을 사용할 수 있습니다. 그것들은 훨씬 더 빠릅니다.

리플렉션 코드는 일반적으로 느립니다

리플렉션 코드에 필요한 동적 메서드 조회 및 형식 검사는 일반 메서드 호출보다 느립니다. 값싼 한 줄의 게터를 반사 짐승으로 바꾸면 (이것을 측정하지는 않았 음) 여러 단계의 속도 저하를보고있을 것입니다.

일반적인 게터 / 세터의 단점

클래스에 더 이상 캡슐화가 없기 때문에 이것은 나쁜 생각입니다. 모든 필드에 액세스 할 수 있습니다. 그것들을 모두 공개 할 수도 있습니다.


8
당신은 모든 주요 포인트를 쳤다. 거의 완벽한 답변입니다. 게터와 세터가 퍼블릭 필드보다 조금 더 낫다고 생각할 수는 있지만 더 큰 점은 맞습니다.
JimmyJames

2
또한 IDE에서 getter / setter를 쉽게 생성 할 수 있다는 점도 언급 할 가치가 있습니다.
stiemannkj1 1

26
+1 프로덕션 규모의 프로젝트에서 디버깅 리플렉션 로직은 UN에 의해 ​​고문으로 인식되고 불법화되어야합니다.
착오 주의자

4
"이 프로그래머들에게는 이해하기 어렵다." -이 프로그래머들에게는 이해하기가 상당히 어렵지만 아무리 능숙하더라도 모든 사람이 이해하기는 어렵 습니다.
BartoszKP

4
"반사 코드는 정적 분석에 액세스 할 수 없습니다"-이것은. 리팩토링을 활성화하는 데 중요합니다. 정적 분석 도구가 더욱 강력 해짐에 따라 (예 : 최신 Team Foundation Server의 코드 검색) 더욱 가치있는 도구를 작성할 수있는 코드를 작성합니다.
Dan J

11

통과 게터 / 세터는 가치를 제공하지 않는 가증이기 때문입니다. 사용자가 속성을 통해 실제 값을 얻을 수 있으므로 캡슐화를 제공하지 않습니다. 필드 스토리지의 구현을 거의 변경하지 않을 것이기 때문에 YAGNI입니다.

그리고 이것은 성능이 떨어지기 때문입니다. C #에서는 적어도 getter / setter가 단일 CIL 명령어에 바로 인라인됩니다. 당신의 대답에서, 당신은 그것을 3 개의 함수 호출로 바꾸고, 다시 반영하기 위해 메타 데이터를로드해야합니다. 나는 일반적으로 반사 비용으로 경험의 원칙으로 10x를 사용했지만 데이터를 검색했을 때 첫 번째 답변 중 하나에 1000x에 가깝게 공을 던지는 이 답변이 포함되었습니다 .

(그리고 코드를 디버깅하기가 더 어렵게 만들고 잘못 사용하는 더 많은 방법이 있기 때문에 사용성에 해를 끼치며 적절한 식별자가 아닌 마술 문자열을 사용하기 때문에 유지 관리 성이 손상됩니다 ...)


2
이 질문에는 Java라는 태그가 붙어 있으며 Beans 개념과 같은 유쾌한 쓰레기가 있습니다. 빈에는 공개 필드가 없지만 리플렉션을 통해 찾을 수있는 필드 getX()setX()메소드를 노출 할 수 있습니다. 빈은 반드시 그들의 가치를 캡슐화 할 필요는 없으며 단지 DTO 일 수도 있습니다. 따라서 Java에서는 게터가 종종 필요한 고통입니다. C # 속성은 모든면에서 훨씬 훌륭합니다.
amon

11
C # 속성은 차분한 게터 / 세터입니다. 그러나 Beans 개념은 재앙입니다.
Sebastian Redl

6
@amon 맞아, C #은 끔찍한 생각을 가지고 구현하기가 더 쉬워졌다.
JimmyJames

5
@JimmyJames 정말 끔찍한 아이디어는 아닙니다. 코드 변경에도 불구하고 ABI 호환성을 유지할 수 있습니다. 많은 사람들이 가지고 있지 않은 문제라고 생각합니다.
Casey

3
@Casey 이것의 범위는 주석을 넘어서지 만 구현의 내부 세부 사항에서 인터페이스를 만드는 것은 거꾸로입니다. 절차 적 디자인으로 이어집니다. getX 메소드를 사용하는 것이 정답 일 때가 있지만 잘 설계된 코드에서는 드문 경우입니다. Java에서 getter 및 setter의 문제점은 주변의 상용구 코드가 아니며 끔찍한 디자인 방식의 일부입니다. 더 쉽게 만드는 것은 얼굴에 더 쉽게 펀치하는 것과 같습니다.
JimmyJames

8

이미 제시된 주장 외에도.

예상되는 인터페이스를 제공하지 않습니다.

사용자는 foo.get ( "bar") 및 foo.set ( "bar", value)가 아니라 foo.getBar () 및 foo.setBar (value)를 호출해야합니다.

사용자가 기대하는 인터페이스를 제공하려면 각 속성에 대해 별도의 getter / setter를 작성해야합니다. 일단 반사를 사용하면 아무런 의미가 없습니다.


이 대답이 다른 답변보다 다른 모든 것보다 더 중요하다고 생각됩니다.
Esben Skov Pedersen

-4

물론 나쁜 아이디어는 아닙니다. 일반적인 자바 세계보다 나쁜 아이디어입니다 (게터 또는 세터 만 원할 때). 예를 들어 일부 프레임 워크 (spring mvc) 또는 librares는 이미 그런 식으로 그것을 사용합니다 (jstl), 일부 json o xml 파서는 그것을 사용하고 있습니다 .DSL을 사용하려면 DSL을 사용하십시오. 그런 다음 사용 사례 시나리오에 따라 다릅니다.

사실 옳고 그른 것은 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.