2016년 8월 28일 일요일

[퍼온글]Inner class에서 local variable에 접근하려면 final이어야 하는 이유

안드로이드 개인 프로젝트를 진행하는 중에 옆에 있던 동기 누나가 생긴 오류로 코드를 들여다보았다.
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);      
        fragmentManager=getFragmentManager();
        fragmentTransaction=fragmentManager.beginTransaction();
        ContentsViewFragment contentsViewFragment=new ContentsViewFragment();
        fragmentTransaction.add(R.id.fragment_container,contentsViewFragment);
        fragmentTransaction.commit();
        // 1. Action bar에서 navigation drawer toggle버튼을 클릭하면 navigation list가 나옴
        //    -> navigation list에 eventlistener 설정
        //    -> selectCategory()
        upperToolbar=(Toolbar)findViewById(R.id.upper_toolbar);
        setSupportActionBar(upperToolbar);
        // 2. 하단에 Action bar로 home, search, write, mypage 버튼 구성 및 eventㅣistener로 각 fragment로 연결
        //    -> selectMenu()
        //    -> home : ContentsViewFragment
        //    -> search: before search; BeforeSearchFragment, after search; ContentsViewFragment, action bar 검색어 입력 모드로 연결
        //    -> write: WriteCategoryFragment
        //    -> mypage: MyPageFragment
        //bottomToolbar는 standalone으로 구현
        bottomToolbar=(Toolbar)findViewById(R.id.bottom_toolbar);
        bottomToolbar.inflateMenu(R.menu.menu_bottom_bar);
        bottomToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener(){
@Override
public boolean onMenuItemClick(MenuItem item) {
        switch(item.getItemId()) {
        case R.id.home:
        ContentsViewFragment contentsViewFragment=new ContentsViewFragment();
        fragmentTransaction.replace(R.id.fragment_container,contentsViewFragment);
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.commit();
        break;
        case R.id.search:
        BeforeSearchFragment beforeSearchFragment=new BeforeSearchFragment();
        fragmentTransaction.replace(R.id.fragment_container,beforeSearchFragment);
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.commit();
        break;
        case R.id.write:
        WriteCategoryFragment writeCategoryFragment=new WriteCategoryFragment();
        fragmentTransaction.replace(R.id.fragment_container,writeCategoryFragment);
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.commit();
        break;
        case R.id.mypage:
        MyPageFragment myPageFragment=new MyPageFragment();
        fragmentTransaction.replace(R.id.fragment_container,myPageFragment);
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.commit();
        break;
        }
    return true;
  }
});
}
        });

 activity_main이 fragment로 화면을 바꾸어 주는데, 아래에 Toolbar로 변경할 수 있도록 구성했다. onMenuItemClick()에 switch문에서 id값에 따라 fragment를 바꿔준다. 코드를 보면 setOnMenuItemClickListener() 안에 new Toolbar.OnMenuItemClickListener() 로 Anonymous class가 선언되고 그 클래스의 onMenuItemClick() method 내에서 switch 구문을 구현한 것이다. 

코드를 실행하니 Inner class에서 local variable에 선언 된 변수에 접근하려니 에러가 발생했는데 그 내용이 'Inner class가 local variable에 접근하고 있다. 해결하려면 local variable을 final로 선언해야 한다' 라는 내용이었다.
 onMenuItemClick()의 switch 구문에서 onCreate()에서 선언된 contentsViewFragment 변수에 접근하면서 생긴 오류였다.

그 이유가 무엇일까 찾아보다가 깊이 있는 분석이 필요함을 느꼈다.

먼저 Inner Class의 개념에 대해 정확히 파악할 필요가 있었다. Java에는 Nested Class 가 있는데, 이 안에 Inner Class와 Lamda Expression이 포함된다. Inner Class는 다시 Local Class와 Anonymous Class가 있다. Java에서는 왜 Nested Class가 필요해졌으며, Inner Class에는 Local Class와 Anonymous Class가 분류되었을까? 이것에 대한 이해가 없이는 그 아래에 대한 이해가 힘들 것 같았다.

Nested Class가 왜 등장하게 되었는지 살펴보니
1. It is a way of logically grouping classes that are only used in one place
 개념적으로 하나의 클래스 B가 오직 다른 클래스 A에서만 유용하다면, B 클래스는 A 클래스 안에서 사용하는 것이 좋다는 것이다. 굳이 다른 곳에서 쓰는 일이 없는데 클래스로 빼낼 필요가 있을까라는 말인 듯 하다. 클래스로 만든 다는 것은 재사용성을 높이기 위함이 있으니까..

2. It increases encapsulation
 B 클래스가 A 클래스의 멤버에 접근해야 한다. 그러려면 public으로 선언하거나 protected로 선언해서 상속을 받아야 할 것이다. 하지만 inner class로 선언해두면 private으로 선언할 수 있다. 그리고 B class는 안에 있기 때문에 외부(outside world)로부터 숨을 수 있다.

3. It can lead to more readable and maintainable code
 좀 더 가독성이 좋고 유지보수하기 쉬운 코드가 된다고 한다. 

Nested Class는 2개의 카테고리로 나뉘는데 static nested class와 non-static nested class(inner class)로 나뉜다. Inner class는 enclosing class의 멤버가 private이더라도 접근할 수 있다.



Inner class의 경우 추가적으로 2가지 타입이 더 있다. Inner class는 method 안에서 선언할 수도 있는데, method 안에 선언하면 local class라고 부르고, class name 없이 method body 안에 선언하면 anonymous class라고 부른다.

Local class는 enclosing class의 멤버에 접근할 수 있다. method 내에 선언하는 class를 local class라고 하니 정확히는 method 내에 선언 된 변수를 의미하는 듯 하다. 그런데 Local class는 오직 final로 선언된 local variable에만 접근할 수 있다. 
"When a local class accesses a local variable or parameter of the enclosing block, it captures that variable or parameter."

Local Class가 local variable 또는 parameter에 접근(Java 8부터는 parameter에도 접근할 수 있다)하려고 하면 그 변수를 captured 한다는데 정확히 무슨 뜻인지 모르겠다. 복사한다는 뜻일까?

Anonymous Class도 Local Class와 마찬가지로 enclosing class의 member에 접근을 하려면 final로 선언이 되 있어야 한다고 했다.




이제 JVM 내부에서 어떻게 동작이 되길래 final에 접근하는지에 대한 고민이 필요했다.

http://www.slipp.net/questions/278 에서도 같은 고민의 글이 있어서 참고를 했다.

위에 읽은 글과 블로그의 내용을 바탕으로 이해한 것은 Anonymous Class나 Local Class에서 local variable에 접근을 하면 그 변수 값을 복사(captured)해서 사용하기 때문에 2개의 변수가 생겨서 값이 불일치 하는 상황이 생길 수 있다. 따라서 final로 선언해서 그런 일을 방지하려고 하는 것이다.
 위의 블로그에는 thread safe에 관해서도 얘기를 하고 있는데 거기까지는 이해가 되지 않고 있다. 좀 더 자세히 생각해봐야 할 것 같다.

[출처]http://ybin.tistory.com/8

자바 클래스 종류

Nested Class

  • Innter Class
    • Local Class
    • Anonymous Class
  • Lamda Expression



2016년 8월 20일 토요일

[AWS]Interactive Tutorial - AWS IoT





AWS IoT 는 연결된 장치들이 -- 자동차, 전구, 감지기 연결망 등 -- 쉽고 안전하게 클라우드 애플리케이션과 다른 장치과 상호작용할 수 있도록 하는 관리되는 클라우드 서비스이다.

이 대화형의 튜토리얼은 다음 서비스 특성들을 보여줌으로써 빠르게 시작하는데 도움을 줄 것이다:
  • 사물을 Device Gateway에 연결하기
  • Rules Engine으로 데이터를 처리하고 작업하기
  • Device Shadows를 가지고 장치 상태를 읽고 설정하기

이 대화형의 튜토리얼로 AWS IoT가 어떻게 동작하는지 배우기

사물을 Device Gateway에 연결하기

AWS IoT Device Gateway는 장치들이 안전하고 효율적으로 AWS IoT와 통신할 수 있게 해준다. 여기를 클릭해서 AWS IoT에 의해 지원되는 프로토콜에 대해 더 많은 것을 배워라.

사물들은 Device Gateway를 통해 각각 통신할 수 있다. 심지어 그들이 다른 프로토콜을 사용하고 있더라도

왼쪽 예제는 두 가지를 보여주고 있다 -- 연결된 전구와 제어 장치 -- 두 가지 모두 Device Gateway에 연결되어 있다. 제어 장치는 Device Gateway로 명령을 발행할 수 잇다. 그리고 전구는 관련있는 명령을 구독 신청하고 들을 수 있다.

Gateway를 통해 어떻게 명령이 전달되는지 보기 위해 제어장치로부터 명령을 발행해 보아라.

R, G, B 버튼을 클릭하면 전구의 색상이 변경된다.


Rules Engine을 가지고 데이터를 처리하고 작업하기

rules engine은 AWS IoT로 들어오는 발행된 메시지들을 평가하고 그것을 변형해서 다른 사물이나 당신이 정의한 business rules에 기초한 클라우드로 전달한다.

rules engine에 대해 더 많이 알려면 여기를 클릭해라.

왼쪽의 예제는 다음의 rule을 묘사하고 있다:
  • 제어 장치로부터 발행된 명령을 평가한다.
  • 명령이 "B"인지 판단한다.
  • 만약 명령이 "B"이면, rule은 메시지를 "G"로 변경하고 "G"를 전구에 전달한다.
rule engine이 메시지를 어떻게 처리하는지 관찰하기 위해 제어장치로 부터 "R""G""B" 명령을 보내 보아라.



Rules Engine을 가지고 데이터를 처리하고 작업하기

rules engine은 또한 메시지를 AWS Lambda functions 이나 DynamoDB table 과 같은 클라우드 endpoints 로 전달할 수도 있다. 여기를 클릭해서 rules를 통해 실행할 수 있는 더 많은 액션들에 대해 알아보아라.

왼쪽의 예제는 두번째 rule의 추가를 보여준다:
  • 제어장치로부터 발행된 명령을 평가한다.
  • 명령이 "R"인지 판단한다.
  • 만약 명령이 "R"이면 명령의 복사본을 세개의 endpoints로 전달한다 -- DynomoDB database table, Lambda compute function, 그리고 모바일 장치로 알림을 푸시하기 위한 Simple Notification Service(SNS)
Rules engine이 메시지를 어떻게 처리하는지 관찰하기 위해 "R""G""B" 명령을 제어장치로부터 보내 보아라. 또한 각 rule을 활성화하거나 비활성화해 보아라.


Device Shadows를 가지고 장치 상태를 읽고 설정하기

AWS Iot는 Device Registry와 Device Shadows를 가지고 있다. 그러면 당신은 이름과 몇몇 속성, 그리고 지속적인 가상의 'shadow'를 가지고 클라우드 상에 표시하고자 하는 어떤 사물을 등록할 수 있다.

왼쪽의 예제는 클라우드 상에 가상의 대상물을 가진 물리적인 전구를 표시하도록 생성된 사물을 보여준다.

물리 전구를 꺼 보아라. 당신은 Device Shadow가 물리 전구의 색상을 기억하고 있다는 것을 알게될 것이다.

다음으로 물리 전구가 여전이 꺼진 상태에서 제어장치로부터 새로운 "R""G""B" 명령을 보내어 보아라.

shadow는 전구의 바라는 미래 상태를 유지할 것이다. 당신이 물리전구를 다시 켰을 때 AWS IoT는 물리 전구에게 그것의 shadow와 매치되도록 명령할 것이다.


Shadow 를 가지고 장치 상태를 읽고 셋팅하기

AWS IoT 는 당신의 연결된 사물과 상호동작하는 companion application의 생성을 쉽게 한다.

왼쪽의 예제는 당신의 전구 색상을 반영하는 모바일 애플리케이션을 보여준다. 모바일 앱은 결코 전구와 직접 통신하지 않는다. 정확히 말해 모바일 앱은 REST API를 사용해서 전구의 Device Shadow의 상태를 읽고 셋팅한다.

모바일 앱을 통해 전구와 상호작용해 보아라.


시작하기

이 대화형식의 튜토리얼은 AWS IoT의 다음 특성들에 대해 설명한다:

  • 사물을 Device Gateway에 연결하기
  • Rules Engine으로 데이터를 처리하고 작업하기
  • Device Shadows를 가지고 장치 상태를 읽고 설정하기
이제 당신은 자신만의 사물을 생성하여 시작할 수 있다.

Getting started guide를 읽거나 이 튜토리얼을 반복해서 더 많은 것을 배울 수 있다.

[퍼온글]초보 개발자가 꼭 알아 두어야 할 다섯 가지 기술들

초보 개발자가 꼭 알아 두어야 할 다섯 가지 기술들

 오늘은 아무도 가르쳐 주지 않는, 그리고 이제 와서 누군가 에게 물어보기도 뻘쭘한 초보 개발자를 탈출하기 위해 필요한 테크닉에 대해 이야기 해 보고자 한다.

 초보 개발자가 고급 개발자가 되어가는 과정을 한마디로 정의하자면 "좀 더 게을러지기 위한 강렬하고 적극적인 의지의 표현"이 되겠다. "생산성"이니 "효율성", "정확성"같은 것 들은  프로그래머에게 있어서 부수적으로 얻어지는 것일 뿐 목적이 될 수 없다. 오로지 끊임없이 편하고 게을러지기 위한 노력만이 있을 뿐이다.


1. 마우스 안 쓰기

오늘날의 컴퓨팅 환경은 시간과 청각과 같은 인지 영역에 의거한 인간의 자연스러운 본능에 모든 행동이 정의되고 제약되는 게슈탈트 심리학의 세계이다. 그러니까 버튼은 누르고 싶게 생겨야 하고 이벤트 알람은 귀에 거슬리게 하여 주의를 끌 수 있어야 한다. 하지만 프로그래밍의 세계는 추상의 세계이다. 보이지 않는 본질을 파악하고 이를 다시 추상화 해 나가는 작업의 반복인 것이다.
 이러한 이유로 필자는 마우스(터치패드나 트랙패드를 포함) 안 쓰기를 초보 프로그래머에게 있어서 익혀야 할 기술의 첫번째로 이름을 올렸다.

 결론부터 말하자면 마우스는 프로그래머의 적이다. 마우스는 인체공학적인 면에 있어서도 손가락만 움직이면 되는 키보드와는 그 편의성과 정확성,속도면 에서 엄청나게 차이가 날 뿐만 아니라, 심리학에서 말해지는 몰입(flow) 상태를 방해 한다는 점에서도 프로그래머와는 궁합이 좋지 않다.

명필은 붓을 가리지 않는다고 하지만
손에 맞는 키보드 선택은 개발자에게 무척 이나 중요하다.
하지만 키보드 사는데 수 십 만원 썼다고 하면 사람들은 내가 음악가 인줄 알겠지?

 그럼에도 불구하고 마우스를 사용하게 되는 것은 순전히 직관에 끌리는 사람의 본능 때문 이다. 오늘날의 컴퓨팅 환경은 직관, 즉 시각과 청각을 최대한 활용하도록 설계되고 있다. 그러니까 마우스를 사용하지 않는 다는 것은 자연스러운 본능과의 싸움인 것이다. 하지만 걱정하지 말라, 결국 컴퓨팅 환경을 만들어내고 있는 것 들 또한 프로그래머 이며, 이들은 자신이 만든 프로그램에 키보드 사용자를 배려한 여러 장치를 해 놓는다. 단축키와 매크로 같은 것들 말이다.

 일단 오늘부터라도 작업에 주로 사용하는 어플리케이션의 주요 기능들을 반드시 단축키를 사용해서 진행하는 습관을 들여보자. IDE뿐만 아니라 워드나 오피스, 그리고 윈도우와 같은 OS 관련 단축키도 포함해서 말이다. 상급 개발자라고 하는것은 무언가 대단한 이치를 깨달은 사람이 아닌 별거 아닌 작은 습관들을 몸에 익힌 사람들일 뿐이다.



2. Command Line Interface

 상징적인 의미 이외에 현실적으로도 Command Line Interface(이하CLI)는 프로그래머가 보다 적극적이고 강렬하게 게을러지기 위해 꼭 필요한 중요한 도구이다. CLI는 동일한 작업의 반복 수행 뿐만 아니라 바로 위에서 언급한 마우스 사용의 억제에도 큰 도움이 된다.

대표적인 CLI화면인 Dos Prompt 


  CLI는 쉘 자체로도 다양한 작업이 가능하지만 AWK나 GREP와 같은 툴을 함께 사용한다면 엄청난 시너지 효과를 낼 수 있다는 사실도 잊지 말자. 윈도우의 경우 powershell을 사용한다면 DOS스타일의 bat보다 훨씬 다양한 작업이 가능하지만,  cygwin이나 unix utils를 이용한 유닉스 스타일의 쉘 작업을 익혀 둔다면,  윈도우 뿐만 아니라 유닉스, 리눅스는 물론이오 BSD를 기반으로 한 Mac OS에서도 요긴하게 써 먹을 수 있다.

 어찌 되었든, 무조건 하루에 두 번 이상 행하는 작업들은 CLI를 이용해 자동화 할 궁리를 하자!




3. 정규표현식

 부끄러운 이야기지만 필자 또한 정규 표현식을 제대로 다룰 수 있게 된 것은 비교적 최근의 일이다. 물론, 그 이전에도 정규표현식을 전혀 사용하지 않은 것은 아니지만 대부분 구글 검색에 의존해 완성된 식을 가져다가 파라메터를 바꿔서 쓰는 수준이었다. 변명을 하자면, 초보 개발자 시절 처음 접한 Perl 코드의 미칠듯한 정규표현식 해석 경험이 일종의 트라우마로 남아 있었던 듯 하다.

 이 정규표현식은 텍스트 데이터를 다루는데 있어서 엄청난 편의성을 제공한다. 특히 개발 도중 자주 직면하게 되는 복잡한 검색이나 치환 작업 들을 다루는데 있어서 대단히 편리한 도구이며, 게을러지기 위해서는 약간의 수고를 들여 꼭 익힐 필요가 있는 기술임에 의심의 여지가 없다.

 초보 개발자들에게는 다소 어려울 수 도 있지만 대부분의 이 바닥의 기술들이 그러하듯이 알고 나면 별거 아니다. 게다가 요즘에는 친절한 웹 튜토리얼들이 넘쳐난다!


  •  RegexOne : 온라인 정규표현식 튜토리얼


4. 터미널 기반 텍스트 에디터

 이클립스나 IntelliJ, Sublime, Brackets와 같은 뛰어난 IDE가 넘처나는 시대이지만 아직까지 텍스트 편집기나 코딩 툴 로서 ViEditor(이하 Vim)이나 Emacs를 애용하는 개발자가 많이 있다. 텍스트 환경 위에서 움직이는 이러한 툴들은 위에서 언급한 키보드만 이용한 작업이라던지 터미널 상의 CLI인터페이스와 궁합이 좋기 때문이다.

 필자는 20년 가까이 vi를 사용해 오다가 얼마전에 Emacs로 넘어간 변절자 종 한 사람 이지만 양쪽 다 개발자에게 훌륭한 에디터 라고 생각한다. emacs를 사용한다 하더라도 vi는 대부분의 unix/bsd계열 os에서 기본 텍스트 에디터로 채용하고 있으므로 기초적인 사용법은 꼭 익혀 두도록 하자.




5. 구글 파워 서칭

 응? 이 양반이 초보라고 사람 무시하나? 아무리 초보 개발자라고 해도 구글 못 쓰겠냐고?
 아니다. 개발자에게는 개발자 다운 구글 사용법이 따로 있다.



 구글은 오늘날 프로그래머에게 있어서 문제 해결의 단서를 찾는 데에 절대 빼 놓을 수 없는 도구이지만 의외로 구글이 제공하는 강력한 검색 옵션 기능들을 제대로 사용하는 개발자는 많지 않다.

 속는 셈 치고 구글에서 제공하는 파워 Power Searching과 Advanced Power Searching 강의를 수강 해 보자. 아니 최소한 무슨 내용을 다루는지 만이라도 살펴보자. 단언컨데, 적은 노력으로 큰 편의를 제공할 것이다.


[원본글]
http://www.moreagile.net/2014/12/HowtoBecomeAnExpertDeveloper.html