2017년 12월 5일 화요일

Intelij 설정 및 plugin

1. preferences... (settings...)


Appearance & Behavior > Appearance - Window Options

        Show memory indicator


Editor > General - Mouse
       Change font size (Zoom) with Ctrl + Mouse Wheel


Editor > General > Appearance

     ✓ Show line numbers


Editor > General > Console
    ✓ Override console cycle buffer size : 10240

Editor > General > Editor Tabs - Tab Closing Policy
 - Tab limit : 99

Editor > General > Editor Tabs - ✓ Mark modified tabs with asterisk (수정파일탭에 * 표시)

Editor > General > Editor Tabs - When closing active editor : 활성 편집기를 닫을 때
       Activate most recently opened tab : 가장 최근에 열린 탭 활성화

Editor > Font
    Font : D2Coding

Editor > File Encodings (변경하려는 charset 으로 3곳 수정 - Global, Project, Propertiers )
      ✓ Transparent native-to-ascii conversion (properties 한글을 java에서 사용가능하도록)

Editor > Code Style
     Right margin (columns) : 120
     x Wrap on typing ( uncheck 자동 줄바꿈 해제함) 

Editor > Code Style > Java > Wrapping and Braces
     ✓ Ensure right margin is not exceeded  (코드 reformatting 시 right margin -120- 넘지 않도록)

Editor > Code Style > Java - Tabs and Indents 
     x Use tab character (uncheck 들여쓰기 tab 문자 사용 안함 - space 로)
    tab size : 2
    indent : 2
    continuation indent : 4

Build, Execution, Deployment > Compiler
       Build project automatically

Build, Execution, Deployment > Debugger > Data Views

     ✓ Show values inline  : debugger 의 variable 을 소스 코드에 출력     
     ✓ Show value tooltip : 값을 확인해볼 변수에 마우스 커서를 위치하면 tooltips 으로 값 확인     
     ✓ Show value tooltip on code selection : 변수를 선택(selection) 하면 show variable tooltip


Autoscroll from Source 


[MENU] - Window - Editor Tabs - ✓ Open New Tabs At The End : 새파일 열때 끝에

[MENU] Preferences -> Apperance & Behavior > Appearance

Disable Mnemonics in Menu 체크해제 : context menu 의 하위 메뉴 키보드 접근

2. idea.vmoption

-Xms2048m
-Xmx2048m
-XX:MaxPermSize=512m
-XX:ReservedCodeCacheSize=512m
-XX:+UseCompressedOops
-XX:PermSize=512m
-server
-Duser.name=fall1999y
-Dfile.encoding=UTf-8

3. plugin

Grep Console
JRebel for Intellij
Key Promoter X
Presentation Assistant (disabled)
Tab Shifter
SBT
CheckStyle-IDEA
PMDPlugin
FindBugs-IDEA
QAPlug
QAPlug - Checkstyle
QAPlug - FindBugs
QAPlug - Hammurapi
QAPlug - PMD
JavaDoc (java doc 헬퍼 플러그인)
GsonFormat
Java Stream Debugger
Atlassian Connector for IntelliJ IDE (jira 등 atlassian 제품 연동)
Eclipse Code Formatter
Markdown support
GenerateSerialVersionUID
String Manipulation (네이밍 방식 설정)
Translator (https://plugins.jetbrains.com/plugin/9690-translator)
Ini4Idea (.ini 확장자 인식 - python 외부 환경설정 파일 관례적 확장자 ) 

2017년 11월 21일 화요일

proxy 설정 (for intellij, maven)

외부망 접속시 proxy 를 꼭 거쳐야 하는 환경에서 개발을 할때
browser 에서 접속이 되는 사이트는 다른 프로그램 (예: ide, maven) 에서도 접속이 가능하다.
browser 가 단지 네트워크 연결을 proxy 서버 를 사용하는 것 일 뿐이기 때문이다.

1. proxy 정보 확인


현재 어떤 proxy 서버를 사용하는지에 대한 확인은
ie > 도구 > 인터넷 옵션 > 연결 - LAN 설정을 보면 확인할 수 있다.

browser 이외의 기타 다른 프로그램에서 프록시 정보를 확인하기 위해
자동구성 영역에서 "자동 구성 스크립트 사용" 이 체크되어 있는지 확인하고
체크되어 있으면 자동 프록시 URL 정보로 실제 사용되는 proxy 정보를 확인해야 한다.

확인방법은
자동 proxy url (예:http://test.com/test.pac) 에 browser 로 접속한 후 다운로드 되는 파일을 텍스트 편집기로 열면  실제로 사용되는 proxy 정보를 알수 있다.
보통 아래와 같은 형태로 되어있다.


 function FindProxyForURL(url,host)
   {
     if (host == "www.harvest-books.org")
         return "DIRECT";
 
         return "PROXY myproxy.harvest-books.org:80;
                 PROXY myotherproxy.harvest-books.org:8080; 
                 DIRECT" ;  
   }
 


위와 같은 형식에서 마지막에 return 되는 proxy 주소들이 자동 구성 proxy 에서 사용되는 proxy 주소들이다.

그리고 "자동 구성 스크립트 사용" 이 체크되어 있지 않거나 보조적으로 자동구성영역 하단의 다음 영역에
수동 proxy 정보가 입력되어 있으면 (자동과 수동이 모두 있다면 자동proxy 를 우선적으로 사용한다.)
수동 proxy 정보도 따로 보관해 어플리케이션에서 프록시 설정시 자동 정보로 proxy 연결이 안되면 수동 proxy 정보로도 시도해봐야 한다.


2. intellij proxy 설정

File > settings > Appearance & Behavior > System Settings > HTTP Proxy 에서 
위에서 확인한 프록시 정보로 설정 후 Check connection 으로 접속이 되는지 테스트한다.

File > settings > Plugins 의
[Install JetBrains Plugin...] 이나 [Browse repositories...]  에서도 마찬가지로 
각각의 버튼을 클릭하면 나오는 창의 하단의 [HTTP Proxy Settings... ] 라는 메뉴에서 proxy 설정이 가능하다.

3. maven proxy 설정

maven 의 user settings file 
(예: intellij 일 경우 settings > Build, Execution, Deployment > Maven - User settings file 의 경로 확인, Override 체크박스 선택)
settings.xml 을 아래의 내용으로 생성한다.

    <proxies>
        <proxy>
            <id>생략가능</active>
            <active>true</active>
            <protocol>http</protocol>
            <host>127.255.255.0</host> <!-- proxy 서버 주소 -->
            <port>8080</port> <!-- proxy 서버 port -->
            <username>없으면 생략가능 - 삭제</username> <!--  proxy id -->
            <password>없으면 생략가능 - 삭제</password> <-- proxy passwd-->
            <nonProxyHosts>없으면 생략가능</nonProxyHosts>
        </proxy>
    </proxies>
</settings>
만약 proxy 서버가 여러개라면 위 내용중 <proxy>...</proxy> 내용을 proxy 서버 갯수 만큼 복사해 
id 밑 host 등 해당 proxy 서버 정보로 수정한다.

autohotkey 설정 (like hhkb)

;; 우측 alt 키를 KeyTweak 프로그램을 이용해 MacKeyPad (NumpadClear) 버튼으로 변경

;;;; mouse ;;;;

SetKeyDelay, -1
SetMouseDelay, -1
#UseHook

초기속도=8
가속도=0.3
한계속도=50
현재속도:=초기속도


NumpadClear & h::
NumpadClear & j::
NumpadClear & k::
NumpadClear & l::
위아래값=0
좌우값=0
if (현재속도<=한계속도)
  현재속도+=가속도

if GetKeyState("j", "P") and GetKeyState("LAlt", "P")
  위아래값 := 현재속도
else if GetKeyState("j", "P") and !GetKeyState("LAlt", "P")
  Send, {Blind}{Down}

if GetKeyState("k", "P") and GetKeyState("LAlt", "P")
  위아래값 := -현재속도
else if GetKeyState("k", "P") and !GetKeyState("LAlt", "P")
  Send, {Blind}{Up}

if GetKeyState("h", "P") and GetKeyState("LAlt", "P")
  좌우값 := -현재속도
else if GetKeyState("h", "P") and !GetKeyState("LAlt", "P")
  Send, {Blind}{Left}


if GetKeyState("l", "P") and GetKeyState("LAlt", "P")
  좌우값 := 현재속도
else if GetKeyState("l", "P") and !GetKeyState("LAlt", "P")
  Send, {Blind}{Right}

MouseMove, %좌우값%, %위아래값%,0,R
if (위아래값) OR (좌우값)
  {
  sleep 10
  goto NumpadClear & j
  }
현재속도:=초기속도
return



NumpadClear & Space::
if GetKeyState("LAlt", "P") {
SendEvent {Blind}{RButton down}
KeyWait RAlt
SendEvent {Blind}{RButton up}
} else {
SendEvent {Blind}{LButton down}
KeyWait RAlt
SendEvent {Blind}{LButton up}
}
return

;;;;;;;;;;;;;;;;


;; left (vi style)
;; NumpadClear & h::
;;    Send, {Blind}{Left}
;; Return

;; down (vi style)
;; NumpadClear & j::
;;      Send, {Blind}{Down}
;; Return
 
;; up (vi style)
;; NumpadClear & k::
;;      Send, {Blind}{Up}
;; Return

;; right (vi style)
;; NumpadClear & l::
;;      Send, {Blind}{Right}
;; Return

;; windows lock (win key + L) 비활성화
SetDisableLockWorkstationRegKeyValue( 1 )
return

;; win + l - block
;; regedit HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System // key = DisableLockWorkstation / value dword = 1
;; #l::
;;   MsgBox, Win-L was pressed! ; Arbitrary code here
;; return

;; 우측 windows key + lock 만 기능 활성화
>#l::
  ; RWin-L
  ; Temporary enable locking
  SetDisableLockWorkstationRegKeyValue( 0 )
  sleep, 100
  ; Lock
  DllCall( "User32\LockWorkStation" )
  sleep, 100
  ; Disable locking again
  SetDisableLockWorkstationRegKeyValue( 1 )
return

;; 좌측 win key + 좌측 alt + L : windows lock
<#<!l::
  ; LWin-LAlt-L
  ; Temporary enable locking
  SetDisableLockWorkstationRegKeyValue( 0 )
  sleep, 100
  ; Lock
  DllCall( "User32\LockWorkStation" )
  sleep, 100
  ; Disable locking again
  SetDisableLockWorkstationRegKeyValue( 1 )
return

OnExitSub:
  ; Enable LockWorkStation, because this script is ending (so other applications aren't further disturbed)
  SetDisableLockWorkstationRegKeyValue( 0 )
  ExitApp
return

SetDisableLockWorkstationRegKeyValue( value )
  {
  RegWrite, REG_DWORD, HKEY_CURRENT_USER, Software\Microsoft\Windows\CurrentVersion\Policies\System, DisableLockWorkstation, %value%
  }

// home key remap : 우측alt + u
NumpadClear & u::
     Send, {Blind}{Home}
Return

// end key remap : RAlt + i
NumpadClear & i::
     Send, {Blind}{end}
Return

// pagedown : RAlt + m
NumpadClear & m::
     Send, {Blind}{pgdn}
Return

// Pageup : RAlt +,
NumpadClear & ,::
     Send, {Blind}{pgup}
Return

;; 한영 : ctrl + space
^space::Send, {vk15sc138}

;; capslock : RAlt + tab
NumpadClear & tab::
     Send, {Blind}{capslock}
Return

;; esc : RAlt + `
NumpadClear & `::
     Send, {Blind}{esc}
Return

;; f1 : RAlt + 1
NumpadClear & 1::
     Send, {Blind}{f1}
Return

NumpadClear & 2::
     Send, {Blind}{f2}
Return

NumpadClear & 3::
     Send, {Blind}{f3}
Return

NumpadClear & 4::
     Send, {Blind}{f4}
Return

NumpadClear & 5::
     Send, {Blind}{f5}
Return

NumpadClear & 6::
     Send, {Blind}{f6}
Return

NumpadClear & 7::
     Send, {Blind}{f7}
Return

NumpadClear & 8::
     Send, {Blind}{f8}
Return

NumpadClear & 9::
     Send, {Blind}{f9}
Return

NumpadClear & 0::
     Send, {Blind}{f10}
Return

NumpadClear & -::
     Send, {Blind}{f11}
Return

NumpadClear & =::
     Send, {Blind}{f12}
Return


NumpadClear & backspace::
 GetKeyState, shiftState, LShift
 GetKeyState, ctrlState, LControl
 if shiftState = D
  if ctrlState = D
   Send, +^{esc}
  else
        Send, +{del}
 else
      Send, {Blind}{del}
Return

NumpadClear & \::
 Send, {Blind}{Insert}
Return

LAlt & backspace::
 Send, {del}
Return

NumpadClear & a::
     Send, {Blind}^a
Return

NumpadClear & c::
     Send, {Blind}^c
Return

NumpadClear & d::
     Send, {Blind}^d
Return

NumpadClear & e::
     Send, {Blind}^e
Return

NumpadClear & f::
     Send, {Blind}^f
Return

NumpadClear & o::
     Send, {Blind}^o
Return

NumpadClear & r::
     Send, {Blind}^r
Return

NumpadClear & t::
     Send, {Blind}^t
Return

NumpadClear & x::
     Send, {Blind}^x
Return

NumpadClear & v::
     Send, {Blind}^v
Return

NumpadClear & w::
     Send, {Blind}^w
Return

NumpadClear & s::
     Send, {Blind}^s
Return

NumpadClear & z::
     Send, {Blind}^z
Return

NumpadClear & enter::
     Send, {AppsKey}
Return

intellij for mac 에서 main menu 위치 이동

mac 용 intellij 에서 main menu 는 mac 의 main menu bar 에 위치한다.
이것을 intellij 내부에 포함시키기 위해서는

Help > Edit Custom Properties 메뉴에서
apple.laf.useScreenMenuBar=false 를 추가하면 intellij 내부로 메인메뉴가 위치한다.
(기본값은 true 이다.)

2017년 5월 17일 수요일

effective java 규칙 11. clone을 재정의할 때는 신중하라

객체의 복사기능을 제공 하려면 Object 의 clone method 를 재정의 해야한다.
Object 의 clone 메서드는 protected 로 제한되어 있다. 그리고 모든 class 는 object 를 상속받는다. 
따라서 모든 class 는 자기자신을 복제할 수 있다 -cloneable implements 시-. 하지만 이렇게 구현된 클래스를 사용하게되는 클라이언트 코드에 구현한 클래스의 clone 을 제공 하는 것은 좀더 까다로운 규칙이 존재한다.
여기서 제공이 의미하는것은 어떠한 class 를 구현하고 그 class 를 사용하는 클라이언트 코드에서 그 class 로 생성된 객체의 복사의 기능을 제공 한다는 의미이다.

위에서 언급 되었듯이 Object  의 clone 은 protected 이다. 따라서 클라이언트 코드에서는 (복사를 제공할) 클래스의 clone method 를 호출할 수 가 없다.

그렇기 때문에 복사기능을 제공할 클래스에선 Object 의 clone 메소드를 재정의(override) 해야 하며
재정의시 접근 제한자를 public 으로 변경해야한다. (override 시 더 넓은 허용범위로의 변경은 허용되지만 그반대는 불가)

단순히 이렇게 clone 을 override 하고 그 override 한 메소드에서 object 의 clone 메소드를 호출하게 하면
해당 객체는 CloneNotSupportedException 을 throw 한다.
그 이유는 Object 의 clone 은 native 메서드이고 이 메서드가 실행 되려면 대상 클래스가 Cloneable 이라는 믹스인 인터페이스 - 선택적 기능을 제공한다는 선언 - 로 복제를 허용한다는 사실을 알려야 한다.
다만 재정의한 class 에서 super.clone() 으로 object 의 clone 을 사용하지 않고 생성자로 객체를 생성해 복제와 유사한 효과를 내어 생성된 객체를 반환한다면 - clone 의 일반규약에는 위배되지만 - Cloneable 을 생략할 수 있다.

즉 class 에서 clone method 를 제공하려면
1. Cloneable interface 를 구현 해야하며 - mixin interface (주 기능 이외의 선택적 기능의 제공자로 선언) 로서의 역할
      - Cloneable 선언하지 않을 경우 CloneNotSupportedException 을 throw 한다.
2. Object.clone() 을 Override 해야 한다.
      - 이때 override clone method 의 접근제한자는 public 으로 변경해야만 상속구조 이외의 class (예 : client class) 에서
      clone 메소드를 사용할 수 있다 - Object.clone() 의 접근제한자는 protected 이므로 -
      - super.clone() 호출

[sample]

/**
* Created by fall1999y on 2017. 5. 12. 오전 6:26
* <pre>
* <b>Description</b>
*
* class 에서 clone method 를 제공하려면
* 1. Cloneable interface 를 구현 해야하며 - mixin interface (주 기능 이외의 선택적 기능의 제공자로 선언) 로서의 역할
* - Cloneable 선언하지 않을 경우 CloneNotSupportedException 을 throw 한다.
* 2. Object.clone() 을 Override 해야 한다.
* - 이때 override clone method 의 접근제한자는 public 으로 변경해야만 상속구조 이외의 class (예 : client class) 에서
* clone 메소드를 사용할 수 있다 - Object.clone() 의 접근제한자는 protected 이므로 -
* - super.clone() 호출
*
* Object clone 의 일반규약
* 1. x.clone != x
* 2. x.clone().getClass() == x.getClass() - 필수 조건은 아님 ( override 시 하위 클래스를 return 할 수 도 있다. )
* 3. x.clone().equals(x) - '값'의 동일성을 의미함, 필수 조건은 아님 ( 기본적으로 object 의 equals 는 객체 주소를 비교하고 재정의시 구현에 따라 참이 아니게 될 수 도 있다.)
* 4. 어떤 생성자도 호출되지 않는다. - final class 에서는 생성자를 호출해도 무방하다.
* </pre>
*/
public class CloneSample implements Cloneable {

private String var;

public CloneSample(String var) {
this.var = var;
}

/**
* Object 의 clone method 의 return type 은 Object 이지만
* override method 의 return type 이 Object 가 아닌 class 로 형변환하므로
* 클라이언트에서 형변환하는 수고를 덜 수 있다.
* - 라이브러리가 할 수 있는 일을 클라이언트에게 미루지 말아야 더 좋은 코드
*
* * override 시 접근제한자의 더 넓은 허용범위로 변경은 허용된다.
* * override 시 return type 은 하위 클래스로 변경 가능하다 (여기서는 Object -> CloneSample 로 return)
* - jdk 1.5 이후
* - 제네릭의 공변 반환형 (covariant return type)
* - override(재정의) 메서드의 반환값 자료형은 재정의되는 메서드의 반환값 자료형의 하위 클래스가 될 수 있다.
* - 아래와 같은 코드로 현재 클래스의 하위 자료형을 return 할 수 도 있다
* return (CloneSampleExt) new CloneSampleExt("a");
*
* @return
* @throws CloneNotSupportedException
*/
@Override
public CloneSample clone() throws CloneNotSupportedException {
// 재정의할 때는 반드시 super.clone 을 호출해 얻은 객체를 반환해야 원하는 클래스의 객체가 생성
// 상속구조의 모든 클래스가 super.clone 을 호출한다면 최종적으로 Object 의 clone 메서드를 호출하게 됨
return (CloneSample) super.clone();

// 아래 코드는 clone 메서드의 일반 규약 '어떤 생성자도 호출되지 않는다' 에 위배되지만 - final class 에서의 재정의에는 - 충분히 실행 가능한 코드
// 그렇지만 비 final class 에서 clone 을 재정의할 때는 반드시 super.clone 을 호출해 얻은 객체를 반환 해야해야만 이후에 상속받는 class 에서
// clone override 코드에서 super.clone 을 했을때 현재 클래스 객체가 반환될 거라는 가정을 충족 시킬수 있다.

// return (CloneSampleExt) new CloneSampleExt("a");
}

public static void main(String[] args) throws CloneNotSupportedException {
CloneSample c = new CloneSample("test");

Object c1 = c.clone();

System.out.println(c1);
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("var", var)
.toString();
}
}


clone 를 override 시 지켜야 할 일반 규칙은 아래와 같다.

1. x.clone != x
2. x.clone().getClass() == x.getClass() - 필수 조건은 아님 ( override 시 하위 클래스를 return 할 수 도 있다. )
3. x.clone().equals(x) - '값'의 동일성을 의미함, 필수 조건은 아님 ( 기본적으로 object 의 equals 는 객체 주소를 비교하고 재정의시 구현에 따라 참이 아니게 될 수 도 있다.)
4. 어떤 생성자도 호출되지 않는다. - final class 에서는 생성자를 호출해도 무방하다.

만약 클래스의 모든 필드가 기본 자료형이거나 변경 불가능 객체 참조라면 위와 같이 구현하기만 하면 된다.

하지만 참조 변수가 있을때는 좀더 복잡한 과정이 필요하다.
복잡한 과정을 살펴보기전에 다음을 이해해야 한다.
"clone 메서드는 또 다른 형태의 생성자이며 이 메서드로 인해 원 객체를 손상시키면 안되고 복사본의 불변식도 제대로 만족시켜야 한다.”
effective java 에선 위와 같이 clone 을 정의 하고 있으며 위 정의를 만족하는 clone 메서드를 재정의하라고 한다.
참조변수는 reference 주소”값"이 저장되어 있고 기본자료형은 “값” 이 저장되어 있다.
object clone 은 값을 복사하는 native method 이며 참조변수가 가지고 있는 값인 주소를 복사 - Shallow Copy - 하는 object clone 이 동작하는 방식이 이치에 어긋나지 않다. 

하지만 책에서의 정의를 만족하려면 - 우리가 기대하는 복사라는 기능을 만족하려면 - 
실제 값의 복사 - deep copy - 과정이 필요하다.
따라서 배열이나 객체를 담고 있는 변수는 clone override 시 추가적인 작업이 필요하다.



private Object[] elemetns;
private List list;
private ArrayList array;

@Override
protected Object clone() throws CloneNotSupportedException {
CloneTest obj = (CloneTest) super.clone();
// 참조형 변수를 위한 추가적인 작업
obj.elemetns = elemetns.clone();
obj.array = (ArrayList) array.clone();

// List 는 interface 이기 때문에 clone 메서드가 없다. 따라서 아래와 같은 코드는 불가능하다
// obj.list = list.clone();
// 아래와 같이 해줘야 한다
obj.list = (List) ((ArrayList) list).clone();

return obj;
}

그리고 직접구현한 배열형 자료구조의 원소가 객체형일때는 각 원소도 copy 를 지원할 수 있도록 만들어야 한다. - deep copy

이렇게 clone 의 대해 알아봤다. 하지만 effective java 에선 이러한 복잡한 clone 메서드가 정말 필요한지 반문한다.
책의 결론은 차라리 객체를 복사할 대안을 제공하거나 아에 복제 기능을 제공하지 말라고 조언한다.
책에서 제시하는 clone 의 대안은
복사 생성자
public Yum(Yum copy) {
    this.some1 = copy.some1;
    ...
}
또는 복사 팩토리 메서드를 제시하고 있다.
public static Yum newInstance(Yum yum) {
    ...
}

위 방법으로 언어 외적 객체 생성수단 (native method) 에 의존하지 않으며 여러 제약 조건에 자유로워 진다.

결론은
Cloneable 을 계승하는 인터페이스는 만들지 말아야 하며, 계승 목적으로 설계하는 클래스는 Cloneable을 구현하지 말아야 한다.

 

2017년 5월 10일 수요일

effective java 규칙 10. toString 은 항상 재정의하라

규칙을 설명하기 전에 toString 의 목적을 알아보자.
toString 메서드의 목적은 객체의 정보를 문자화 시키는데 있다.
여기서 객체의 정보란 인스턴스 변수에 저장된 값을 뜻한다.

우리는 이러한 toString() 를 사용함으로써 logging 등에 유용하게 사용할 수 있다.
이외의 용도는 논의 할 필요성이 있다. 
임의의 비즈니스 로직에서 객체의 toString 결과를 사용하는 코드가 필요한지는 의문이 든다. 
(비즈니스를 처리하는 로직은 별도의 메소드를 만들어 처리해야 할 것 같다.)


이렇게 객체의 정보를 문자화 시키는 Object 클래스의 toString public method 는 다음과 같이 정의되어 있다.

public String toString() {
    return this.getClass().getName() + "@" + Integer.toHexString(this.hashCode());}

기본 결과 형태가 나타내는 정보는 위의 정의에서 나타나듯이 해당 클래스의 이름 + @ (구분자) +  hashCode 형태이다.
이 결과로 얻을수 있는 유용한 정보는 객체의 클래스 명 정도이다.
hashCode 는 이전 규칙( rule 9) 에서 언급 했듯이 hash 알고리즘으로 계산된 결과이고
같은 객체에 대해선 같은 hashCode 가 보장되지만 다른 객체에선 항상 다른 hashCode 값이 보장이 되지 않으므로 객체의 식별자 역할을 하기에는 부족하다. (참고용 정도?)

그렇기 때문에 toString method 를 객체가 가지고 있는 인스턴스 변수를 표현하도록 override 해야 한다.

이쯤에서 toString() 의 일반규약을 알아보자.
1. 사람이 읽기 쉽도록 간략하지만 유용한 정보를 제공
2. 모든 하위 클래스는 이 메서드를 재정의함이 바람직하다.

위와 같은 일반규약을 가지고 있고 책(effective java) 에서는 객체 내의 중요 정보를 전부 담아 반환 (많을 경우 요약정보로 반환) 하는 것을 권장한다.

그리고 저자는 toString 구현시 그 포멧을 문서에 명시하는 것에 대해 설명하고 있고 
(예:bigInteger, BigDecimal 등 boxing class.. / 그리고 값 클래스는 형식 명시 권장함)
형식을 명시 하지 않을 경우에도 (명시 할 때도 마찬가지로) 그 의도는 문서에 남겨야 한다고 한다.
(이런 입장 역시 -내 생각과는 다르게- toString 의 활용처를 좀더 넓게 보고 있는 것이다.)

의도를 문서에 남기는 것은 로깅시에도 필요한 경우가 많기 때문에 동의할 수 있는 부분이다.
하지만 포멧을 지정한다는 (주석으로 명시 한다는 문제가 아니라 비즈니스 로직에 사용하기 위해 포멧을 지정 한다는) 것은
내 개발 스타일에는 맞지 않는 것 같다.

그리고 toString 이 반환하는 정보는 별도의 접근자 (method 등.. getter)를 가져야 한다.
그렇지 않으면 해당 클래스를 사용하는 프로그래머는 toString 을 파싱해서 정보를 가져오도록 시도할 것이다.
(toString()  이 api 화됨)
이런 시도는 성능상에 불이익 및 오류등 안전하지 못한 시스템을 만들 가능성이 높아진다.
- 생각해 볼 문제 : 
그렇다면 클라이언트 코드에서 사용하지 못하도록 private 선언한 변수는 toString 에서 그 정보가 나타나지 않도록 해야 하는가?
그 정보가 로깅에 중요한 정보라도? - 협업의 공통 rule 로써 관리해야 할 것 같다. toString 의 활용법 제한등

- toString method 호출
* print 관련 메서드에서 객체를 사용할 때  (println, printf 등..)
System.out.println(c);


* 문자열 연결 연산자의 피 연산자로 사용될 때
String c = "a"+t;


* assert 구문이 실행될때
※ assert는 특정조건이 참일 때만 프로그램이 계속된다.
이러한 assert 구문은 java 실행시 vm option 에 -ea 를 주어야 적용된다.

assert c==null : t;

* 디버거에 객체가 전달될때


위와 같은 경우에 객체의 toString 메서드가 자동 호출 된다.


2017년 5월 6일 토요일

effective java 규칙 9. equals를 재정의할 때는 반드시 hashCode 도 재정의하라.

Object 의 hashcode 

Object 의 hash code 는 heap 에 생성되는 객체의 주소(reference 주소)를 바탕으로 생성된다.
따라서 객체의 주소가 다르다면 (값은 같을 지라도) 같은 hash code 는 가질 수 없다.

hashcode 의 목적

객체를 구별하는데 그 목적이 있다.
구별이란 주소(reference)의 동치 판단의 의미와 의 동치 판단으로 구분된다.

객체를 구별하는데 그 객체가 저장된 주소 가 동일한 즉 실제로 똑같은 같은 메모리상 위치에 저장된 같은 객체인지를 판단하려면
Object 의 기본적인 hash code 를 사용하면된다.

그러나 객체의 중요필드의 값이 같다면 같은 객체라고 판단하는 비즈니스 로직이 있을 경우
hashcode 는 equals 메소드와 마찬가지로 override 되어야 한다.
그래야만 hashcode 메소드의 일반규약을 사용하는 클래스에서 제대로된 작동을 보장할 수 있다. (HashMap 이나 HashTable 등..)

hashCode() 의 일반규약

두 객체의 equals 의 결과가 true 이면 hashcode 는 동일
두 객체의 equals 의 결과가 false 이더라도 hashcode 는 항상 다르지는 않다. = 객체가 같지 않더라도 hashcode 는 동일할 수 있다
    - hashcode 란 계산된 결과(숫자) 이기 때문에 계산 알고리즘 (Hashing Algorith) 이 항상 완전한 해시 함수를 구현하지 않으면 
    hashcode 는 동일 할 수 있다.
    - Boolean 같이 서로 구별되는 객체의 종류가 적거나 Number 형 객체는 객체가 나타내려는 값 자체를 해시 값으로 사용할 수 있기 때문에 완전한 해시 함수 대상이 되지만 String 이나 pojo 에 대하여 완전한 해시 함수를 제작하는 것은 사실상 불가능
    - 그러나 거의 대부분 서로 다른 hashCode 값이 나오면 hashcode 를 사용하는 collections 에서 성능이 향상된다.

※ 완전한 해시 함수
x.equals(y) 가 false 일때 x.hashCode() != y.hashCode() 가 항상 성립할때

hashCode() override 시 가장 중요한 핵심 규약은 hashCode() 일반 규약의 첫번째 설명 이다.
-같은 객체는 같은 해시 코드값을 가진다.-

hashCode 지침
다음은 effective java 에서 제안하는 hashCode 지침이다.
   1. 초기값으로 0이 아닌 int 상수를 지정
  1. 중요 필드값을 number 형태의 결과로 나타날수 있도록 계산
    • boolean 일경우 f ? 1: 0
    • int 보다 범위가 작은 타입(byte,char,short,int)일 경우 (int) f 
    • long 일 경우 (int) (f ^ (f >>> 32))
    • float : Float.floatToIntBits(f) , double : Double.doubleToLongBits(f)
    • 객체참조 일때 hashCode 메서드를 재귀적으로 호출하여 계산, null 일 경우 0 반환
    • 배열일때 각 원소별 계산 후 result = 31 * result + c 로 결합 - c는 계산값 result 는 초기값(누산값)
      • Arrays.hashCode 메서드 가운데 하나를 사용 가능
    • 결합 : result = 31 * result + c 로 결합 - c는 계산값 result 는 초기값(누산값)
      • 31 의미 : 소수이면서 홀수 , 분포도를 좋게 하기 위해 소수를 사용, Mersenne prime
      • * 곱셈의 의미 : (String 해시함수가 곱셈이 없이 단순 누산이였다면 순서만 바뀐 문자열의 해시 코드가 동일해짐)
  2. result 반환
  3. 점검 - 동치 관계일때 같은 해시 코드 값인지 확인 및 테스트
  4. 중보필드는 계산과정에서 제외, 다른 필드에서 유도될 수 있는 필드 제외

그외 검토할 사항

변경불가능 객체의 hashCode 계산시 계산비용이 높다면 캐시에 저장할 필요성
    대부분의 객체가 해시 키로 사용된다면 객체를 생성할 때 해시 코드를 계산
    그렇지 않다면 hashCode 값의 초기화 지연 기법

성능 개선목적으로 중요 필드의 계산 과정을 누락시키면 안됨 
    - 해시값 품질이 저하되어 해시 테이블의 성능을 매우 떨어뜨림

샘플코드


private int a;
private int b;
private int c;

public HashCodeTest(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}

// 해시코드 초기화 지연 기법 사용시
private volatile int hashCode;
@Override
public int hashCode() {

int result = hashCode;
// 초기화 지연 기법 적용
if(result == 0) {
result = 17;
result = 31 * result + a;
result = 31 * result + b;
result = 31 * result + c;

hashCode = result;
}

return result;

}

Intelij 설정 및 plugin

1. preferences... (settings...) Appearance & Behavior > Appearance - Window Options        ✓   Show memory indicator Editor ...