다음은 Josh Bloch의 효과적인 Java 규칙보다 미묘한 10 가지 Java 코딩 관행 목록입니다. 학습하기 쉽고 일상적인 상황에 중점을 둔 Josh Bloch의 목록과 비교할 때이 목록에는 API/SPI 디자인의 드문 일이 포함 된 상황이 포함되어있어 큰 영향을 줄 수 있습니다.
Jooq (Java에서 SQL 모델 내부 DSL)를 작성하고 유지하는 동안이 문제를 만났습니다. 내부 DSL로서 Jooq는 Java 컴파일러와 제네릭에 가장 큰 범위에 도전하여 제네릭, 변이 가능한 매개 변수 및 오버로드를 결합하여 Josh Bloch가 이러한 광범위한 API에 권장하지 않을 수 있습니다.
Java 코딩을위한 10 가지 미묘한 모범 사례와 공유하겠습니다.
1. C ++의 소멸자를 기억하십시오
C ++의 파괴자를 기억하십니까? 기억 나지 않니? 그런 다음 객체 삭제가 해제되지 않은 후 메모리가 할당되어 메모리 누출을 디버깅 할 필요가 없기 때문에 정말 운이 좋습니다. 구현 된 쓰레기 수집 메커니즘에 대한 Sun/Oracle에게 감사합니다!
그럼에도 불구하고, 소멸자는 흥미로운 기능을 제공합니다. 자유 메모리에 대한 역 할당 순서를 이해합니다. 클래스 소멸자 구문을 운영 할 때 Java의 경우입니다.
다른 다양한 사용 사례가 있습니다. 다음은 일부 이벤트 리스너의 SPI를 구현하는 방법에 대한 구체적인 예입니다.
@overridepublic void preverevent (eventContext e) {super.beforeevent (e); // 내 코드 전 슈퍼 코드} @OverRidePublic void efdevent (eventContext e) {// 코드 후 슈퍼 코드 Super.AfterEvent (e);} 악명 높은 철학자의 식사 문제는 그것이 중요한 이유에 대한 또 다른 좋은 예입니다. 철학자 식사에 대한 질문은 링크를 확인하십시오.
http://adit.io/posts/2013-05-11-dining-philosophers-problem-with-ron-swanson.html
규칙 : 전/후, 할당/무료, 사용/반환 시맨틱을 사용할 때마다 논리를 구현하기 위해 Semantics를 가져 가거나 반환 할 것인지, 후/무료/반환 작업을 역 순서로 수행할지 고려하십시오.
고객에게 SPI 방법을 제공하여 라이브러리/코드에 사용자 정의 동작을 쉽게 주입 할 수 있습니다. SPI 진화 판단이 당신을 혼란스럽게 할 수 있음을주의하십시오. 추가 매개 변수가 필요하지 않다고 생각합니다. 물론 기능을 너무 일찍 추가해서는 안됩니다. 그러나 SPI를 게시하면 시맨틱 버전을 따르기로 결정한 후에는 다른 매개 변수가 필요한 경우 SPI에 멍청한 단일 매개 변수를 추가하는 것을 후회하게됩니다.
인터페이스 eventListener {// bad void message (string message);}메시지 ID와 메시지 소스가 필요하면 어떻게됩니까? API 진화는 위의 유형에 매개 변수를 추가하지 못하게합니다. 물론 Java 8을 사용하면 초기에 나쁜 디자인 결정을 "방어"하는 수비수 방법을 추가 할 수 있습니다.
인터페이스 eventListener {// 잘못된 기본 void 메시지 (문자열 메시지) {message (message, null, null); } // 더 나은? void message (문자열 메시지, 정수 ID, 메시지 소스);} 불행히도 수비수 방법은 최종 수정자를 사용할 수 없습니다.
그러나 많은 방법을 사용하여 SPI를 오염시키는 것보다 컨텍스트 객체 (또는 매개 변수 개체)를 사용하는 것이 훨씬 좋습니다.
인터페이스 MessageContext {String Message (); 정수 ID (); messageSource source ();} 인터페이스 eventListener {// 굉장한! void message (messagecontext context);} MessageContext API를 EventListner SPI보다 쉽게 발전시킬 수 있습니다.
규칙 : SPI가 지정 될 때마다 고정 된 매개 변수가있는 메소드를 작성하는 대신 컨텍스트/매개 변수 객체를 사용하는 것을 고려하십시오.
참고 : 빌더 API를 사용하여 구성 할 수있는 전용 MessagerESult 유형을 통해 결과를 바꾸는 것이 좋습니다. 이것은 SPI 진화의 유연성을 크게 증가시킬 것입니다.
스윙 프로그래머는 일반적으로 수백 또는 수천 개의 익명 클래스를 생성하기 위해 몇 가지 단축키 키를 누르십시오. 대부분의 경우, 인터페이스를 따르고 SPI 하위 유형 수명주기가 위반되지 않는 한 해치지 않습니다. 그러나 간단한 이유로 익명, 지역 또는 내부 클래스를 자주 사용하지 마십시오. 외부 클래스에 대한 참조를 저장합니다. 어디로 가든지 외부 카테고리가 따라야합니다. 예를 들어, 로컬 클래스의 외부 작동이 부적절한 경우 전체 객체 그래프가 미묘한 변경을 거쳐 메모리 누출이 발생할 수 있습니다.
규칙 : 익명, 로컬 또는 내부 클래스를 작성하기 전에 정적 또는 일반 최상위 클래스로 변환 할 수 있는지 두 번 생각하여 객체를 외부 수준 도메인으로 반환하는 방법을 피하십시오.
참고 : 이중 곱슬 브레이스를 사용하여 간단한 물체를 초기화하십시오.
new Hashmap <string, String> () {{put ( "1", "a"); put ( "2", "b");}}이 메소드는 JLS §8.6 사양에 설명 된 인스턴스 이니셜 라이저를 사용합니다. 표면에서는 좋아 보이지만 실제로는 권장되지 않습니다. 완전히 독립적 인 해시 맵 객체를 사용하는 경우 인스턴스가 항상 외부 객체에 대한 참조를 보유하지는 않습니다. 또한 클래스 로더가 더 많은 클래스를 관리 할 수 있습니다.
Java8의 페이스가 다가오고 있습니다. Java 8을 사용하면 Lambda 표현이 마음에 들거나 좋아하든 아니든 람다 표현을 제공합니다. API 사용자가 좋아할 수도 있지만 가능한 한 자주 사용할 수 있는지 확인하는 것이 좋습니다. 따라서 API가 int, long, string 및 date와 같은 간단한 "스칼라"유형을받지 않으면 API가 가능한 한 자주 SAM을 받도록하십시오.
샘은 무엇입니까? Sam은 단일 추상 방법 [유형]입니다. 함수 인터페이스라고도하는 것은 곧 @functionalInterface로 주석을 달게됩니다. 이것은 규칙 2와 일치하고 EventListener는 실제로 SAM입니다. 최고의 SAM은 람다 표현의 글쓰기를 더욱 단순화 할 것이기 때문에 하나의 매개 변수 만 있습니다. 글쓰기를 상상해보십시오
Listeners.add (c-> system.out.println (c.message ());
교체하려면
LEASSERS.ADD (new EventListener () {@override public void omestmes (messagecontext c) {system.out.println (c.message ())); }});Joox에서 XML을 처리한다고 상상해보십시오. Joox에는 많은 Sam이 포함되어 있습니다.
$ (document) // id .find (c -> $ (c) .id ()! = null)을 가진 요소를 찾습니다. // 자녀를 찾으십시오. children (c-> $ (c) .tag (). equals ( "order")) // 모든 일치 (c-> system.out.println ($ (c))).
규칙 : API 사용자에게 친절하고 지금부터 Sam/Function 인터페이스를 작성하십시오.
참고 : Java8 Lambda 표현식 및 개선 된 컬렉션 API에 대한 흥미로운 블로그가 많이 있습니다.
나는 Java Nulls에 관한 1 개 또는 2 개의 기사를 작성했으며 Java 8에서 새로운 선택적 수업을 소개하는 것을 설명했습니다. 학문적이거나 실용적인 관점에서 이러한 주제는 매우 흥미 롭습니다.
NULL과 NULLPOINTEREXCEPTion은 여전히이 단계에서 Java의 결함이지만 여전히 문제가없는 API를 설계 할 수 있습니다. API를 설계 할 때는 사용자가 체인에서 메소드를 호출 할 수 있으므로 NULL을 최대한 반환하지 않아야합니다.
초기화 (someargument) .calculate (data) .dispatch ();
위 코드에서 볼 수 있듯이 방법은 NULL을 반환해서는 안됩니다. 실제로, 널 사용은 일반적으로 비슷한 이질성으로 간주됩니다. jQuery 또는 Joox와 같은 도서관은 반복 가능한 물체에 완전히 널을 버렸습니다.
NULL은 일반적으로 초기화 지연에 사용됩니다. 대부분의 경우 성능에 심각하게 영향을 미치지 않으면 서 지연 초기화를 피해야합니다. 실제로 관련된 데이터 구조가 너무 크면 지연 초기화를주의해서 사용해야합니다.
규칙 : 방법은 그럴 때마다 널 리턴을 피해야합니다. NULL은 "비 초기화"또는 "존재하지 않는"의 의미론을 나타내는 데만 사용됩니다.
어떤 경우에는 널 값이 널 값이있는 메소드를 반환해도 괜찮지 만 빈 배열이나 빈 컬렉션을 반환하지 마십시오! java.io.file.list () 메소드를 참조하십시오. 다음과 같이 설계되었습니다.
이 메소드는 지정된 디렉토리의 모든 파일 또는 디렉토리에 대한 문자열 배열을 반환합니다. 디렉토리가 비어 있으면 반환 된 배열이 비어 있습니다. 지정된 경로가 존재하지 않거나 I/O 오류가 발생하면 return null.
따라서이 방법은 일반적으로 다음과 같이 사용됩니다.
file directory = // ... if (directory.isdirectory ()) {String [] list = directory.list (); if (list! = null) {for (문자열 파일 : list) {// ...}}}Null Check가 필요하다고 생각하십니까? 대부분의 I/O 작업은 IOExceptions를 생성하지만이 방법은 NULL 만 반환합니다. NULL은 I/O 오류 메시지를 저장할 수 없습니다. 따라서 이러한 디자인에는 다음과 같은 세 가지 단점이 있습니다.
컬렉션의 관점에서 문제를 살펴보면 빈 배열 또는 컬렉션이 "Not Endess"를 가장 잘 구현하는 것입니다. 빈 배열 또는 컬렉션을 반환하는 것은 초기화 지연에 사용되지 않는 한 실질적인 의미는 없습니다.
규칙 : 반환 된 배열 또는 컬렉션은 무효가되어서는 안됩니다.
HTTP의 이점은 무국적입니다. 모든 관련 상태는 각 요청 및 응답에 전송됩니다. 이것은 휴식 명명의 본질입니다 : 상태 이전 (표현 상태 이전). 자바에서 이것을하는 것도 좋습니다. 메소드가 상태 매개 변수 객체를 수신 할 때 규칙 2의 관점에서 이것에 대해 생각하십시오. 상태가 외부에서 상태를 운영하기보다는 그러한 물체를 통해 전송되는 경우 상황이 더 쉬울 것입니다. JDBC를 예로 들어 보겠습니다. 다음 예제는 저장된 프로그램의 커서를 읽습니다.
Callabestatement S = Connection.prepareCall ( "{? = ...}"); // 문장의 조작 상태 : s.registerOutParameter (1, cursor); s.setString (2, "ABC"); s.execute (); resultSet rs = s.getObject (1); // verbose state : rs.next ();이것은 JDBC API를 너무 이상하게 만듭니다. 각 객체는 상태가 있고 작동하기가 어렵습니다. 특히 두 가지 주요 문제가 있습니다.
규칙 : 기능 스타일의 더 많은 구현. 메소드 매개 변수를 통해 상태를 전송합니다. 운영 객체 상태는 거의 없습니다.
이것은 비교적 쉬운 작동 방법입니다. 비교적 복잡한 객체 시스템에서 모든 객체의 equals () 메소드에서 동등한 판단을 내리는 한 상당한 성능 향상을 달성 할 수 있습니다.
@overridepublic boolean Equals (Object Other) {if (this == Other) true; // 기타 평등 판단 논리 ...}다른 단락 검사에는 널 값 점검이 포함될 수 있으므로 다음을 추가해야합니다.
@overridepublic boolean Equals (Object Other) {if (this == Other) true; if (Other == null) false를 반환합니다. // 나머지 평등 논리 ...}규칙 : 성능을 향상시키기 위해 모든 평등 () 방법에서 단락을 사용하십시오.
어떤 사람들은이 방법에 동의하지 않을 수 있습니다. 왜냐하면 메소드 기본값을 최종적으로 기본으로 만드는 것은 Java 개발자의 습관과 상반되기 때문입니다. 그러나 코드를 완전히 제어하는 경우 메소드 기본값을 최종으로 만드는 것이 정확합니다.
이것은 정적 방법에 특히 적합하며,이 경우 "오버레이"(실제로 폐색)은 거의 효과가 없습니다. 나는 최근 Apache Tika에서 매우 나쁜 음영 정적 방법의 예를 발견했습니다. 봐 :
TikainputStream은 TaggedInputStream을 확장하여 비교적 다른 구현으로 정적 get () 메소드를 마스킹합니다.
기존 메소드와 달리 통화가 컴파일 시간에 정적 메소드 호출에 바인딩되므로 정적 메소드를 서로 덮어 쓸 수 없습니다. 운이 좋지 않으면 실수로 잘못된 방법을 얻을 수 있습니다.
규칙 : API를 완전히 제어 할 경우 최종 기본값을 최종 최종적으로 만듭니다.
특별한 경우에는 "accept-all"변수 매개 변수 메소드를 사용하여 객체를 수신 할 수 있습니다 ... 매개 변수 :
void acceptall (객체 ... 모두);
이러한 방법을 작성하면 Java 생태계에 약간의 JavaScript 느낌이 생깁니다. 물론 String…와 같은 실제 상황에 따라 실제 유형을 제한 할 수 있습니다. 너무 많이 제한하고 싶지 않기 때문에 객체를 일반 t로 바꾸는 것이 좋은 생각이라고 생각할 수 있습니다.
void acceptall (t ... 모두);
그러나 그렇지 않습니다. t는 항상 물체로 추론됩니다. 실제로, 당신은 위의 방법에서 제네릭을 사용할 수 없다고 생각할 수 있습니다. 더 중요한 것은 위의 방법을 과부하 할 수 있다고 생각할 수도 있지만 할 수는 없습니다.
void acceptall (t ... all); void acceptall (문자열 메시지, t ... all);
이는 선택적으로 문자열 메시지를 메소드에 전달할 수있는 것 같습니다. 그러나이 전화는 어떻게됩니까?
acceptall ( "메시지", 123, "ABC");
컴파일러는 t로 <? 시리얼이 가능하고 비슷한 <? >>를 확장하여 호출을 불분명하게 만듭니다!
따라서 "수락"서명이있을 때마다 (일반적인 경우에도), 당신은 그것을 지나치게 과부하 할 수 없을 것입니다. API 소비자는 컴파일러가 "우연히"올바른 "방법을 선택할 수있게 할 수 있습니다. 그러나 수락 방법을 사용하거나 다른 방법을 호출하지 않을 수도 있습니다.
규칙 : 가능하면 "수락"서명을 피하십시오. 그렇지 않은 경우 그러한 방법에 과부하하지 마십시오.
자바는 짐승입니다. 보다 이상적인 언어와는 달리, 그것은 오늘날의 상황으로 천천히 진화했습니다. Java 개발 속도에 이미 수백 가지의 경고가 있으며 이러한 경고는 수년간의 경험을 통해서만 파악할 수 있기 때문에 이것은 좋은 일입니다.
이 주제에 대한 상위 10 개 목록 중 더 많은 것을 기대하십시오!
원본 링크 : jooq 번역 : importnew.com- 유사성
번역 링크 : http://www.importnew.com/10138.html
[재판 할 때 원래 소스, 번역기 및 번역 링크를 유지하십시오. ]]