Top 10 useful, yet paranoid Java programming techniques
시간날 때 추후 번역https://jaxenter.com/top-10-useful-yet-paranoid-java-programming-techniques-119498.html
1. 문자 값을 앞에 넣어라
다음과 같은
equals()
비교문의 왼쪽에 문자 값을 넣으면 가끔씩 발생하는 NullPointerException 을 방지하는데 것도 결코 나쁘지 않은 생각이다 :// Bad if (variable.equals("literal")) { ... } // Good if ("literal".equals(variable)) { ... }
이건 생각할 필요도 없다. 좋은 버전에서 더 좋은걸 사용 하기 위해 표현을 바꾼다고 해도 달라지는 건 없다.
그냥 다르게도 표현 할 수 있다는 걸 알았잖아? 다른 의견...
2. 초기의 JDK API들을 믿지 말라
Java 초기에는, 프로그램을 한다는 건 큰 고통이었다. API들은 여전히 부실했고 이 같은 코드 조각들이 난무했다:
String[] files = file.list(); // Watch out if (files != null) { for (int i = 0; i < files.length; i++) { ... } }
편집증 같아보이나? 하지만, Javadoc을 읽어보라:
이 추상(abstract)경로명이 디렉토리를 나타내지 않는다면, 이 메소드는 null을 반환한다. 그렇지 않다면 디렉토리 안의 각각의 파일이나 디렉토리를 의미하는, 하나의 문자들의 배열이 반환된다.
맞다. 그냥 좀 더 확인을 위해서 하나더 추가를 한다면:
if (file.isDirectory()) { String[] files = file.list(); // Watch out if (files != null) { for (int i = 0; i < files.length; i++) { ... } } }
안됐군! 우리가 아는 Java 코딩시 미묘한 모범 사례(10 Subtle Best Practices when Coding Java)의 5,6 번을 위반했다. 그래서 null 에대한 확인을 추가해야 했다!
3. “-1”을 믿지 마라
난 이건 편집증 적이라 생각한다. Javadoc의
String.indexOf()
는 다음과 같이 분명하게 말한다...이 오브젝트(반환되는)의 나열된 문자에서 처음으로 나오는 문자의 위치를 나타내거나,문자가 나오지 않는다면 -1을 반환한다.
그래서,
-1
이 괜찮다고 할 수 있나? 아니다. 이걸 생각해 보라:// Bad if (string.indexOf(character) != -1) { ... } // Good if (string.indexOf(character) >= 0) { ... }
Who knows. Perhaps they’ll need ANOTHER encoding at some point in time to say, the
otherString
would have been contained if checked case-insensitively… Perhaps a good case for returning -2
? Who knows.
모를일이지만. 만약 같은위치에서 다른 인코딩이 필요한, 말하자면, 대소문자를 구별해야 줘야하는게 포함된
otherString
이 있다고 하면... 아마 -2를 반환하는게 좋은 예시일까? 모를일이다.
(의미를 잘 모르겠음.)
결국,
NULL
이 되는 10억달라짜리 실수에 대한 10억개의 토론을 하는거다. 근본적인 형식인 int에 대해서 null
대신 - 을 사용하는게 맞는지,-1에 대한 토론을 해야 시작해야 하나?4. 돌발적인 할당을 피하라
그래. 최고로 많이 일어난다(난 아니다. 7번을 보라).
(이게 JavaScript라고 가정하고, 그냥 언어로서 편집증 적이 되어 보자)
// Ooops if (variable = 5) { ... } // Better (because causes an error) if (5 = variable) { ... } // Intent (remember. Paranoid JavaScript: ===) if (5 === variable) { ... }
반복. 구문에 문자가 있다면, 왼쪽에 두자. =표시가 하나더 붙었다고 느껴도, 여기선 실수라고 생각안한다.
5. null과 길이를 확인하라
Whenever you have a collection, array, etc., make sure it’s present AND not empty.
// Bad if (array.length > 0) { ... } // Good if (array != null && array.length > 0) { ... }
You never know where those arrays come from. Perhaps from early JDK API?
6. 모든 메소드들은 final이다
You can tell me all you want about your open/closed principles, that’s all bollocks. I don’t trust you (to correctly extend my classes) and I don’t trust myself (to not accidentally extend my classes). Which is why everything that is not explicitly intended for subtyping (i.e. only interfaces) is strictly
final
. See also item #9 of our 10 Subtle Best Practices when Coding Java list.// Bad public void boom() { ... } // Good. Don't touch. public final void dontTouch() { ... }
Yes. It’s final. If that doesn’t work for you, patch it, or instrument it, or rewrite the byte code. Or send a feature request. I’m sure that your intent of overriding the above isn’t a good idea anyway.
7. 모든 변수들과 파라메터들은 final이다
As I said. I don’t trust myself (to not accidentally overwrite my values). Having said so, I don’t trust myself at all. Because…
… which is why all variables and parameters are made
final
, too.// Bad void input(String importantMessage) { String answer = "..."; answer = importantMessage = "LOL accident"; } // Good final void input(final String importantMessage) { final String answer = "..."; }
OK, I admit. This one, I don’t apply very often, really, although I should. I wish Java got it right like Scala, where people just type
val
all over the place, without even thinking about mutability – except when they need it explicitly (rarely!), via var
.8. 오버로드시에는 제너릭을 믿지 마라
Yes. It can happen. You believe you wrote that super nice API which totally rocks and is totally intuitive, and along comes some user who just raw-casts everything up to
Object
until the darn compiler stops bitching, and suddenly they’ll link the wrong method, thinking it’s your fault (it always is).
Consider this:
// Bad <T> void bad(T value) { bad(Collections.singletonList(value)); } <T> void bad(List<T> values) { ... } // Good final <T> void good(final T value) { if (value instanceof List) good((List<?>) value); else good(Collections.singletonList(value)); } final <T> void good(final List<T> values) { ... }
Because, you know… Your users, they’re like:
// This library sucks @SuppressWarnings("all") Object t = (Object) (List) Arrays.asList("abc"); bad(t);
Trust me. I’ve seen everything. Including things like:
It’s good to be paranoid.
9. switch문의 default에는 항상 throw를 사용하라
Switch… One of those funny statements where I don’t know whether to petrify with awe or to just cry. Anyway, we’re stuck with
switch
, so we may as well get it right when we have to. i.e.// Bad switch (value) { case 1: foo(); break; case 2: bar(); break; } // Good switch (value) { case 1: foo(); break; case 2: bar(); break; default: throw new ThreadDeath("That'll teach them"); }
Because that moment where
value == 3
is introduced into the software, it’ll come for sure! And don’t sayenum
, because it’ll happen to enums
as well!10. Switch에 중괄호(curly braces)를 사용하라
In fact,
switch
is the most wicked statement anyone has every allowed to get into a language while they were either drunk or lost a bet. Consider the following example:// Bad, doesn't compile switch (value) { case 1: int j = 1; break; case 2: int j = 2; break; } // Good switch (value) { case 1: { final int j = 1; break; } case 2: { final int j = 2; break; } // Remember: default: throw new ThreadDeath("That'll teach them"); }
Within the
switch
statement, there is only one scope defined among all the case
statements. In fact, thesecase
statements aren’t even really statements, they’re like labels and the switch
is a goto call. In fact, you could even compare case
statements with the astonishing FORTRAN 77 ENTRY statement, a device whose mystery is only exceeded by its power.
This means that the variable
final int j
is defined for all the different cases, regardless if we issue a break
or not. Not very intuitive. Which is why it’s always a good idea to create a new, nested scope per case
statement via a simple block. (but don’t forget the break
within the block!)Conclusion
Paranoid programming may seem weird at times, as code often turns out to be a bit more verbose than really needed. You might think, “oh this is never gonna happen”, but as I said. After 20 years or so programming, you just don’t want to fix those stupid little unnecessary bugs anymore that exist only because the language is so old and flawed. Because you know…
댓글 없음:
댓글 쓰기