현재 버전

뭐라고 답변을 써야 하나...... 질문이 딱 단답으로 하기에는 오해할까봐 걱정이 되네요..

이걸 어떻게 잘 정리해서 답변할수 있을지. 잘 답변하도록 노력해보겠습니다.


2가지 측면에서 말씀을 드려 보겠습니다. 하나는 문법적 측면 두번쨰는 구현하려는 기능에 특성적 측면입니다. 사실 이 두가지가 앞서 달린 답변에 다 있네요.

첫번째 문법적 측면에서 입니다. 자바의 익명클래스와 C#의 익명함수에 대해서 동일시하게 생각하시면 됩니다. 글쓴이분께서 말씀하신 C#의 문법과 자바의 익명클래스는 그 동작의 거의 비슷합니다. 하지만 한가지가 다릅니다. 다른 글에서도 소개했는데 자바의 익명클래스와 C#의 익명함수는 외부의 지역변수를 캡쳐하는데 이때 이들을(익명클래스, 익명함수) 클로저라고 부릅니다. 그리고 자바와 C#의 클로저는 동작이 서로 조금 다릅니다. 정확히는 자바는 제한적인 클로저를 제공합니다. 그 결과가 익명클래스 내에서 캡쳐한 외부 지역변수는 변경할수 없다는 거죠, C#은 변경이 가능합니다. 자바의 이 변경할수 없다는 제약은 사용자가 외부 지역변수의 final키워드를 강요합니다. 그렇지 않으면 컴파일러가 에러를 출력합니다.

(참고로 자바 7? 8? 에서는 실질적으로 final이면 키워드를 쓰지 않아도 컴파일러가 알서 판단합니다.)

문법 소개는 이쯤에서 마치고 자바에서 문법적으로 str을 변경하는 법에 대해서 소개를 해보겠습니다.

외부 지역변수에 대해서 변경할수 없다고 하였는데, 이말은 반대로 하면 str을 익명클래스의 외부 지역변수를 멤버로 가진 외부클래스의 멤버 변수로 사용하는 경우 str의 값을 변경할수 있다는 겁니다.

Class Ouster {

    public String str;

    public String method () {

         new Thread(new Runnable() {

              public void run() {    str =  "";           // 변경   가능 }

         }).start();

        return str;

     }

}

아니면 str을 멤버변수로 가지는 다른 클래스를 정의하여 해당 클래스의 객체를 생성해서 이 객체를 캡처하고 해당 객체의 멤버인 str을 변경하는 방법도 있습니다.

Class Ouster {

    public UserClass method () {

         UserClass user = new UserClass();

         new Thread(new Runnable() {

              public void run() {    user.str =  "";           // 변경   가능 }

         }).start();

        return user;

     }

}

사실 첫번째 방법은 두번째 방법이 되기때문에 가능한겁니다. 자바에서 캡쳐된 변수를 변경하지는 못하는 제약이 있지만 해당 변수의 멤버를 변경하지 못하는건 아닙니다. 이것이 두번쨰 방법이고 익명클래스가 캡쳐하는 지역변수는 기본적으로 this를 포함하고 있습니다. 여기서 this는 외부지역변수를 멤버로 가진 외부클래스 객체를 가리키고요 즉 this객체의 멤버인 str은 변경이 가능한거죠(첫번쨰와 두번째가 같다는 말이 어떤 뜻인지 알겠나요?)


두번재 구현하려는 기능적 측면에서 말씀을 드려보겠습니다. 사실 이게 제일 중요한데 제일 설명하기 어렵네요. 코드를 보니 구현하려는 기능의 특성이 비동기를 구현하려는것 같습니다. 코드를 보니 그러려고 했을거라 유추가 되는거죠. 하지만 반대로 비동기에 대한 특성을 잘 이해하고 구현하려고 하시는건지는 의문이 갑니다. 이유는 잘못된 방법으로 str을 return 하고 있기 때문인데요. 

일단 작성하신 메소드의 내용만 보면 GetWebAPi 메소드는 호출 이후 String을 바로 return하겠죠, 그리고 코드의 실행순서는 바로 쭉 나갈겁니다. 이와 동시에 스레드가 동작하고 str에 내용을 채우겠죠.

즉 str에 내용을 채우는 이 코드과 원래의 코드와 비동기적으로 일어난다는 겁니다. 즉 수행시간이 길어질수 있는 입출력을 수행하는 GetWebAPi 메소드를 호출할때 블락이 되지않도록(즉 해당 코드에서 긴 시간이 소요되어 다음 코드로 넘어 가지 않는) 하는 비동기의 장점을 이용하려는 것처럼 보입니다.

이것은 UserClass result = GetWebAPi()에서 result가 언제 내가 원하는 값으로 바뀔지 알수가 없다는 의미 이기도 하지요. 즉 result.str에 내용을 일정시간마다 확인을 해야겠죠.

비동기 구현 방법은 2가지가 핵심입니다. 수행시간이 길어질수 있는 코드를 분리해 내어 비동기적으로 수행하도록 하는것(위에 스레드를 사용한것이겠죠) 과 그렇게 비동기적으로 수행된 결과값이 있는 경우 또는 비동기적으루 수행된 코드의 완료 여부를 받는것 입니다.(또는 중간 중간 상태 또는 중간 결과를 받는것도 포함되죠). 제가 볼떄 글쓴이 분께서 원하시는건 비동기적으로 수행된 코드가 완료된 시점에 결과를 받는것이 아닐까 생각이 드네요. 그리고 해당 코드에서는 이러한 부분이 부족하고요

또한 작성하신 코드는 비동기를 구현하기 위해 즉 분기를 주기 위해 스레드를 생성하고 있는데, 이것도 성능상 바르지 못합니다. GetWebAPi메소드가 호출되면서 스레드를 생성하는데, 이것보단 미리 생성된 스레드를 이용하는 것이 더 좋습니다.

완료된 결과를 리턴 받는 방법도 2가지가 있는데 이벤트 방식과 동기방식(블락)입니다. 이것들을 지원하는 자바의 API는  gorun999 님이 언급하신 AsyncTask , Callable 로 구현할수 있고, 이미 잘구현된CompletableFuture 도 있습니다. CompletableFuture 나 AsyncTask 를 이용하기를 권하고 싶네요.

C#의 경우 이러한 비동기를 구현하기 위해 문법적으로 제공합니다. async await이죠. 그리고 이것은 제가 앞서 말한것과 비슷한 방식으로 구현이 되어있습니다. 미리 생성된 스레드 풀에 분기된 작업을 수행하도록 하고 이벤트를 발생시키거나 동기(블락)를 하여 결과값을 리턴 받습니다. (C#은 한 3개월? 정도만 해봐서 정확한 내용인지는 모르겠네요. async await를 써본적도 없고, 적은 내용도 그렇게 읽었던 기억이 있어서 쓴거구요. 조금은 틀릴수도 있어요...)


새벽에 써서 그런지 오타 많네요 ㅠ

그리고 한가지 더 이것도 글쓸때는 쓰고 싶었는데 빼먹었네요. 위에서 익명클래스는  기본적으로 this (익명클래스의 this가 아닌 외부클래스의 this 객체)를 포함하고 있다고 하였는데, 이 얘기는 한가지 문제를 야기합니다. 익명클래스를 사용하는 경우 익명클래스의 객체 생명주기가 외부클래스의 객체 생명주기보다 길어지는 경우 익명클래스의 객체 생명주기가 끝날때까지 외부클래스의 객체가 가비지 컬렉트가 되지 않아서 메모리 누수가 됩니다.

이것은 당연한 이야기 이지만,  익명클래스가 this객체의 참조를 갖을때 명시적으로 갖는 것이 아니고 묵시적으로 갖기 때문에 익명클래스가 this객체를 기본적으로 캡쳐한다는 사실을 모른다면 문제가 되겠죠, 그래서 이런걸 신경쓰지 않기 위해서 익명클래스를 사용할떄 static을 사용하던지 익명클래스를 구현한 새 클래스를 사용하는 방법을 사용합니다. 아니면 최근의 방식인 람다를 사용하죠 람다는 static 과 익명클래스를 구현하는 방법보다 더 세련된 방법을 제공합니다. 이것에 관련된거 여기까지 마무리 하겠습니다. 중요한건 익명클래스가 외부클래스의 this객체를 참조하고 있다는 사실을 알고 있어야 한다는 겁니다.




수정 이력

2018-01-18 17:08:40 에 아래 내용에서 변경 됨 #4

뭐라고 답변을 써야 하나...... 질문이 딱 단답으로 하기에는 오해할까봐 걱정이 되네요..

이걸 어떻게 잘 정리해서 답변할수 있을지. 잘 답변하도록 노력해보겠습니다.


2가지 측면에서 말씀을 드려 보겠습니다. 하나는 문법적 측면 두번쨰는 구현하려는 기능에 특성적 측면입니다. 사실 이 두가지가 앞서 달린 답변에 다 있네요.

첫번째 문법적 측면에서 입니다. 자바의 익명클래스와 C#의 익명함수에 대해서 동일시하게 생각하시면 됩니다. 글쓴이분께서 말씀하신 C#의 문법과 자바의 익명클래스는 그 동작의 거의 비슷합니다. 하지만 한가지가 다릅니다. 다른 글에서도 소개했는데 자바의 익명클래스와 C#의 익명함수는 외부의 지역변수를 캡쳐하는데 이때 이들을(익명클래스, 익명함수) 클로저라고 부릅니다. 그리고 자바와 C#의 클로저는 동작이 서로 조금 다릅니다. 정확히는 자바는 제한적인 클로저를 제공합니다. 그 결과가 익명클래스 내에서 캡쳐한 외부 지역변수는 변경할수 없다는 거죠, C#은 변경이 가능합니다. 자바의 이 변경할수 없다는 제약은 사용자가 외부 지역변수의 final키워드를 강요합니다. 그렇지 않으면 컴파일러가 에러를 출력합니다.

(참고로 자바 7? 8? 에서는 실질적으로 final이면 키워드를 쓰지 않아도 컴파일러가 알서 판단합니다.)

문법 소개는 이쯤에서 마치고 자바에서 문법적으로 str을 변경하는 법에 대해서 소개를 해보겠습니다.

외부 지역변수에 대해서 변경할수 없다고 하였는데, 이말은 반대로 하면 str을 익명클래스의 외부 지역변수를 멤버로 가진 외부클래스의 멤버 변수로 사용하는 경우 str의 값을 변경할수 있다는 겁니다.

Class Ouster {

    public String str;

    public String method () {

         new Thread(new Runnable() {

              public void run() {    str =  "";           // 변경   가능 }

         }).start();

        return str;

     }

}

아니면 str을 멤버변수로 가지는 다른 클래스를 정의하여 해당 클래스의 객체를 생성해서 이 객체를 캡처하고 해당 객체의 멤버인 str을 변경하는 방법도 있습니다.

Class Ouster {

    public UserClass method () {

         UserClass user = new UserClass();

         new Thread(new Runnable() {

              public void run() {    user.str =  "";           // 변경   가능 }

         }).start();

        return user;

     }

}

사실 첫번째 방법은 두번째 방법이 되기때문에 가능한겁니다. 자바에서 캡쳐된 변수를 변경하지는 못하는 제약이 있지만 해당 변수의 멤버를 변경하지 못하는건 아닙니다. 이것이 두번쨰 방법이고 익명클래스가 캡쳐하는 지역변수는 기본적으로 this를 포함하고 있습니다. 여기서 this는 외부지역변수를 멤버로 가진 외부클래스 객체를 가리키고요 즉 this객체의 멤버인 str은 변경이 가능한거죠(첫번쨰와 두번째가 같다는 말이 어떤 뜻인지 알겠나요?)


두번재 구현하려는 기능적 측면에서 말씀을 드려보겠습니다. 사실 이게 제일 중요한데 제일 설명하기 어렵네요. 코드를 보니 구현하려는 기능의 특성이 비동기를 구현하려는것 같습니다. 코드를 보니 그러려고 했을거라 유추가 되는거죠. 하지만 반대로 비동기에 대한 특성을 잘 이해하고 구현하려고 하시는건지는 의문이 갑니다. 이유는 어설프게 str을 return 하고 있기 때문인데요. 

일단 작성하신 메소드의 내용만 보면 GetWebAPi 메소드는 호출 이후 String을 바로 return하겠죠, 그리고 코드의 실행순서는 바로 쭉 나갈겁니다. 이와 동시에 스레드가 동작하고 str에 내용을 채우겠죠.

즉 str에 내용을 채우는 이 코드과 원래의 코드와 비동기적으로 일어난다는 겁니다. 즉 수행시간이 길어질수 있는 입출력을 수행하는 GetWebAPi 메소드를 호출할때 블락이 되지않도록(즉 해당 코드에서 긴 시간이 소요되어 다음 코드로 넘어 가지 않는) 하는 비동기의 장점을 이용하려는 것처럼 보입니다.

이것은 UserClass result = GetWebAPi()에서 result가 언제 내가 원하는 값으로 바뀔지 알수가 없다는 의미 이기도 하지요. 즉 result.str에 내용을 일정시간마다 확인을 해야겠죠.

비동기 구현 방법은 2가지가 핵심입니다. 수행시간이 길어질수 있는 코드를 분리해 내어 비동기적으로 수행하도록 하는것(위에 스레드를 사용한것이겠죠) 과 그렇게 비동기적으로 수행된 결과값이 있는 경우 또는 비동기적으루 수행된 코드의 완료 여부를 받는것 입니다.(또는 중간 중간 상태 또는 중간 결과를 받는것도 포함되죠). 제가 볼떄 글쓴이 분께서 원하시는건 비동기적으로 수행된 코드가 완료된 시점에 결과를 받는것이 아닐까 생각이 드네요. 그리고 해당 코드에서는 이러한 부분이 어설프고요

또한 작성하신 코드는 비동기를 구현하기 위해 즉 분기를 주기 위해 스레드를 생성하고 있는데, 이것도 성능상 바르지 못합니다. GetWebAPi메소드가 호출되면서 스레드를 생성하는데, 이것보단 미리 생성된 스레드를 이용하는 것이 더 좋습니다.

완료된 결과를 리턴 받는 방법도 2가지가 있는데 이벤트 방식과 동기방식(블락)입니다. 이것들을 지원하는 자바의 API는  gorun999 님이 언급하신 AsyncTask , Callable 로 구현할수 있고, 이미 잘구현된CompletableFuture 도 있습니다. CompletableFuture 나 AsyncTask 를 이용하기를 권하고 싶네요.

C#의 경우 이러한 비동기를 구현하기 위해 문법적으로 제공합니다. async await이죠. 그리고 이것은 제가 앞서 말한것과 비슷한 방식으로 구현이 되어있습니다. 미리 생성된 스레드 풀에 분기된 작업을 수행하도록 하고 이벤트를 발생시키거나 동기(블락)를 하여 결과값을 리턴 받습니다. (C#은 한 3개월? 정도만 해봐서 정확한 내용인지는 모르겠네요. async await를 써본적도 없고, 적은 내용도 그렇게 읽었던 기억이 있어서 쓴거구요. 조금은 틀릴수도 있어요...)


새벽에 써서 그런지 오타 많네요 ㅠ

그리고 한가지 더 이것도 글쓸때는 쓰고 싶었는데 빼먹었네요. 위에서 익명클래스는  기본적으로 this (익명클래스의 this가 아닌 외부클래스의 this 객체)를 포함하고 있다고 하였는데, 이 얘기는 한가지 문제를 야기합니다. 익명클래스를 사용하는 경우 익명클래스의 객체 생명주기가 외부클래스의 객체 생명주기보다 길어지는 경우 익명클래스의 객체 생명주기가 끝날때까지 외부클래스의 객체가 가비지 컬렉트가 되지 않아서 메모리 누수가 됩니다.

이것은 당연한 이야기 이지만,  익명클래스가 this객체의 참조를 갖을때 명시적으로 갖는 것이 아니고 묵시적으로 갖기 때문에 익명클래스가 this객체를 기본적으로 캡쳐한다는 사실을 모른다면 문제가 되겠죠, 그래서 이런걸 신경쓰지 않기 위해서 익명클래스를 사용할떄 static을 사용하던지 익명클래스를 구현한 새 클래스를 사용하는 방법을 사용합니다. 아니면 최근의 방식인 람다를 사용하죠 람다는 static 과 익명클래스를 구현하는 방법보다 더 세련된 방법을 제공합니다. 이것에 관련된거 여기까지 마무리 하겠습니다. 중요한건 익명클래스가 외부클래스의 this객체를 참조하고 있다는 사실을 알고 있어야 한다는 겁니다.



2018-01-18 09:29:58 에 아래 내용에서 변경 됨 #3

뭐라고 답변을 써야 하나...... 질문이 딱 단답으로 하기에는 오해할까봐 걱정이 되네요..

이걸 어떻게 잘 정리해서 답변할수 있을지. 잘 답변하도록 노력해보겠습니다.


2가지 측면에서 말씀을 드려 보겠습니다. 하나는 문법적 측면 두번쨰는 구현하려는 기능에 특성적 측면입니다. 사실 이 두가지가 앞서 달린 답변에 다 있네요.

첫번째 문법적 측면에서 입니다. 자바의 익명클래스와 C#의 익명함수에 대해서 동일시하게 생각하시면 됩니다. 글쓴이분께서 말씀하신 C#의 문법과 자바의 익명클래스는 그 동작의 거의 비슷합니다. 하지만 한가지가 다릅니다. 다른 글에서도 소개했는데 자바의 익명클래스와 C#의 익명함수는 외부의 지역변수를 캡쳐하는데 이때 이들을(익명클래스, 익명함수) 클로저라고 부릅니다. 그리고 자바와 C#의 클로저는 동작이 서로 조금 다릅니다. 정확히는 자바는 제한적인 클로저를 제공합니다. 그 결과가 익명클래스 내에서 캡쳐한 외부 지역변수는 변경할수 없다는 거죠, C#은 변경이 가능합니다. 자바의 이 변경할수 없다는 제약은 사용자가 외부 지역변수의 final키워드를 강요합니다. 그렇지 않으면 컴파일러가 에러를 출력합니다.

(참고로 자바 7? 8? 에서는 실질적으로 final이면 키워드를 쓰지 않아도 컴파일러가 알서 판단합니다.)

문법 소개는 이쯤에서 마치고 자바에서 문법적으로 str을 변경하는 법에 대해서 소개를 해보겠습니다.

외부 지역변수에 대해서 변경할수 없다고 하였는데, 이말은 반대로 하면 str을 익명클래스의 외부 지역변수를 멤버로 가진 외부클래스의 멤버 변수로 사용하는 경우 str의 값을 변경할수 있다는 겁니다.

Class Ouster {

    public String str;

    public String method () {

         new Thread(new Runnable() {

              public void run() {    str =  "";           // 변경   가능 }

         }).start();

        return str;

     }

}

아니면 str을 멤버변수로 가지는 다른 클래스를 정의하여 해당 클래스의 객체를 생성해서 이 객체를 캡처하고 해당 객체의 멤버인 str을 변경하는 방법도 있습니다.

Class Ouster {

    public UserClass method () {

         UserClass user = new UserClass();

         new Thread(new Runnable() {

              public void run() {    user.str =  "";           // 변경   가능 }

         }).start();

        return user;

     }

}

사실 첫번째 방법은 두번째 방법이 되기때문에 가능한겁니다. 자바에서 캡쳐된 변수를 변경하지는 못하는 제약이 있지만 해당 변수의 멤버를 변경하지 못하는건 아닙니다. 이것이 두번쨰 방법이고 익명클래스가 캡쳐하는 지역변수는 기본적으로 this를 포함하고 있습니다. 여기서 this는 외부지역변수를 멤버로 가진 외부클래스 객체를 가리키고요 즉 this객체의 멤버인 str은 변경이 가능한거죠(첫번쨰와 두번째가 같다는 말이 어떤 뜻인지 알겠나요?)


두번재 구현하려는 기능적 측면에서 말씀을 드려보겠습니다. 사실 이게 제일 중요한데 제일 설명하기 어렵네요. 코드를 보니 구현하려는 기능의 특성이 비동기를 구현하려는것 같습니다. 코드를 보니 그러려고 했을거라 유추가 되는거죠. 하지만 반대로 비동기에 대한 특성을 잘 이해하고 구현하려고 하시는건지는 의문이 갑니다. 이유는 어설프게 str을 return 하고 있기 때문인데요. 

일단 작성하신 메소드의 내용만 보면 GetWebAPi 메소드는 호출 이후 String을 바로 return하겠죠, 그리고 코드의 실행순서는 바로 쭉 나갈겁니다. 이와 동시에 스레드가 동작하고 str에 내용을 채우겠죠.

즉 str에 내용을 채우는 이 코드과 원래의 코드와 비동기적으로 일어난다는 겁니다. 즉 수행시간이 길어질수 있는 입출력을 수행하는 GetWebAPi 메소드를 호출할때 블락이 되지않도록(즉 해당 코드에서 긴 시간이 소요되어 다음 코드로 넘어 가지 않는) 하는 비동기의 장점을 이용하려는 것처럼 보입니다.

이것은 UserClass result = GetWebAPi()에서 result가 언제 내가 원하는 값으로 바뀔지 알수가 없다는 의미 이기도 하지요. 즉 result.str에 내용을 일정시간마다 확인을 해야겠죠.

비동기 구현 방법은 2가지가 핵심입니다. 수행시간이 길어질수 있는 코드를 분리해 내어 비동기적으로 수행하도록 하는것(위에 스레드를 사용한것이겠죠) 과 그렇게 비동기적으로 수행된 결과값이 있는 경우 또는 비동기적으루 수행된 코드의 완료 여부를 받는것 입니다.(또는 중간 중간 상태 또는 중간 결과를 받는것도 포함되죠). 제가 볼떄 글쓴이 분께서 원하시는건 비동기적으로 수행된 코드가 완료된 시점에 결과를 받는것이 아닐까 생각이 드네요. 그리고 해당 코드에서는 이러한 부분이 어설프고요

또한 작성하신 코드는 비동기를 구현하기 위해 즉 분기를 주기 위해 스레드를 생성하고 있는데, 이것도 성능상 바르지 못합니다. GetWebAPi메소드가 호출되면서 스레드를 생성하는데, 이것보단 미리 생성된 스레드를 이용하는 것이 더 좋습니다.

완료된 결과를 리턴 받는 방법도 2가지가 있는데 이벤트 방식과 동기방식(블락)입니다. 이것들을 지원하는 자바의 API는  gorun999 님이 언급하신 AsyncTask , Callable 로 구현할수 있고, 이미 잘구현된CompletableFuture 도 있습니다. CompletableFuture 나 AsyncTask 를 이용하기를 권하고 싶네요.

C#의 경우 이러한 비동기를 구현하기 위해 문법적으로 제공합니다. async await이죠. 그리고 이것은 제가 앞서 말한것과 비슷한 방식으로 구현이 되어있습니다. 미리 생성된 스레드 풀에 분기된 작업을 수행하도록 하고 이벤트를 발생시키거나 동기(블락)를 하여 결과값을 리턴 받습니다. ()


새벽에 써서 그런지 오타 많네요 ㅠ

그리고 한가지 더 이것도 글쓸때는 쓰고 싶었는데 빼먹었네요. 위에서 익명클래스는  기본적으로 this (익명클래스의 this가 아닌 외부클래스의 this 객체)를 포함하고 있다고 하였는데, 이 얘기는 한가지 문제를 야기합니다. 익명클래스를 사용하는 경우 익명클래스의 객체 생명주기가 외부클래스의 객체 생명주기보다 길어지는 경우 익명클래스의 객체 생명주기가 끝날때까지 외부클래스의 객체가 가비지 컬렉트가 되지 않아서 메모리 누수가 됩니다.

이것은 당연한 이야기 이지만,  익명클래스가 this객체의 참조를 갖을때 명시적으로 갖는 것이 아니고 묵시적으로 갖기 때문에 익명클래스가 this객체를 기본적으로 캡쳐한다는 사실을 모른다면 문제가 되겠죠, 그래서 이런걸 신경쓰지 않기 위해서 익명클래스를 사용할떄 static을 사용하던지 익명클래스를 구현한 새 클래스를 사용하는 방법을 사용합니다. 아니면 최근의 방식인 람다를 사용하죠 람다는 static 과 익명클래스를 구현하는 방법보다 더 세련된 방법을 제공합니다. 이것에 관련된거 여기까지 마무리 하겠습니다. 중요한건 익명클래스가 외부클래스의 this객체를 참조하고 있다는 사실을 알고 있어야 한다는 겁니다.



2018-01-18 09:27:41 에 아래 내용에서 변경 됨 #2

뭐라고 답변을 써야 하나...... 질문이 딱 단답으로 하기에는 오해할까봐 걱정이 되네요..

이걸 어떻게 잘 정리해서 답변할수 있을지. 잘 답변하도록 노력해보겠습니다.


2가지 측면에서 말씀을 드려 보겠습니다. 하나는 문법적 측면 두번쨰는 구현하려는 기능에 특성적 측면입니다. 사실 이 두가지가 앞서 달린 답변에 다 있네요.

첫번째 문법적 측면에서 입니다. 자바의 익명클래스와 C#의 익명함수에 대해서 동일시하게 생각하시면 됩니다. 글쓴이분께서 말씀하신 C#의 문법과 자바의 익명클래스는 그 동작의 거의 비슷합니다. 하지만 한가지가 다릅니다. 다른 글에서도 소개했는데 자바의 익명클래스와 C#의 익명함수는 외부의 지역변수를 캡쳐하는데 이때 이들을(익명클래스, 익명함수) 클로저라고 부릅니다. 그리고 자바와 C#의 클로저는 동작이 서로 조금 다릅니다. 정확히는 자바는 제한적인 클로저를 제공합니다. 그 결과가 익명클래스 내에서 캡쳐한 외부 지역변수는 변경할수 없다는 거죠, C#은 변경이 가능합니다. 자바의 이 변경할수 없다는 제약은 사용자가 외부 지역변수의 final키워드를 강요합니다. 그렇지 않으면 컴파일러가 에러를 출력합니다.

(참고로 자바 7? 8? 에서는 실질적으로 final이면 키워드를 쓰지 않아도 컴파일러가 알서 판단합니다.)

문법 소개는 이쯤에서 마치고 자바에서 문법적으로 str을 변경하는 법에 대해서 소개를 해보겠습니다.

외부 지역변수에 대해서 변경할수 없다고 하였는데, 이말은 반대로 하면 str을 익명클래스의 외부 지역변수를 멤버로 가진 외부클래스의 멤버 변수로 사용하는 경우 str의 값을 변경할수 있다는 겁니다.

Class Ouster {

    public String str;

    public String method () {

         new Thread(new Runnable() {

              public void run() {    str =  "";           // 변경   가능 }

         }).start();

        return str;

     }

}

아니면 str을 멤버변수로 가지는 다른 클래스를 정의하여 해당 클래스의 객체를 생성해서 이 객체를 캡처하고 해당 객체의 멤버인 str을 변경하는 방법도 있습니다.

Class Ouster {

    public UserClass method () {

         UserClass user = new UserClass();

         new Thread(new Runnable() {

              public void run() {    user.str =  "";           // 변경   가능 }

         }).start();

        return user;

     }

}

사실 첫번째 방법은 두번째 방법이 되기때문에 가능한겁니다. 자바에서 캡쳐된 변수를 변경하지는 못하는 제약이 있지만 해당 변수의 멤버를 변경하지 못하는건 아닙니다. 이것이 두번쨰 방법이고 익명클래스가 캡쳐하는 지역변수는 기본적으로 this를 포함하고 있습니다. 여기서 this는 외부지역변수를 멤버로 가진 외부클래스 객체를 가리키고요 즉 this객체의 멤버인 str은 변경이 가능한거죠(첫번쨰와 두번째가 같다는 말이 어떤 뜻인지 알겠나요?)


두번재 구현하려는 기능적 측면에서 말씀을 드려보겠습니다. 사실 이게 제일 중요한데 제일 설명하기 어렵네요. 코드를 보니 구현하려는 기능의 특성이 비동기를 구현하려는것 같습니다. 코드를 보니 그러려고 했을거라 유추가 되는거죠. 하지만 반대로 비동기에 대한 특성을 잘 이해하고 구현하려고 하시는건지는 의문이 갑니다. 이유는 어설프게 str을 return 하고 있기 때문인데요. 

일단 작성하신 메소드의 내용만 보면 GetWebAPi 메소드는 호출 이후 String을 바로 return하겠죠, 그리고 코드의 실행순서는 바로 쭉 나갈겁니다. 이와 동시에 스레드가 동작하고 str에 내용을 채우겠죠.

즉 str에 내용을 채우는 이 코드과 원래의 코드와 비동기적으로 일어난다는 겁니다. 즉 수행시간이 길어질수 있는 입출력을 수행하는 GetWebAPi 메소드를 호출할때 블락이 되지않도록(즉 해당 코드에서 긴 시간이 소요되어 다음 코드로 넘어 가지 않는) 하는 비동기의 장점을 이용하려는 것처럼 보입니다.

이것은 UserClass result = GetWebAPi()에서 result가 언제 내가 원하는 값으로 바뀔지 알수가 없다는 의미 이기도 하지요. 즉 result.str에 내용을 일정시간마다 확인을 해야겠죠.

비동기 구현 방법은 2가지가 핵심입니다. 수행시간이 길어질수 있는 코드를 분리해 내어 비동기적으로 수행하도록 하는것(위에 스레드를 사용한것이겠죠) 과 그렇게 비동기적으로 수행된 결과값이 있는 경우 또는 비동기적으루 수행된 코드의 완료 여부를 받는것 입니다.(또는 중간 중간 상태 또는 중간 결과를 받는것도 포함되죠). 제가 볼떄 글쓴이 분께서 원하시는건 비동기적으로 수행된 코드가 완료된 시점에 결과를 받는것이 아닐까 생각이 드네요. 그리고 해당 코드에서는 이러한 부분이 어설프고요

또한 작성하신 코드는 비동기를 구현하기 위해 즉 분기를 주기 위해 스레드를 생성하고 있는데, 이것도 성능상 바르지 못합니다. GetWebAPi메소드가 호출되면서 스레드를 생성하는데, 이것보단 미리 생성된 스레드를 이용하는 것이 더 좋습니다.

완료된 결과를 리턴 받는 방법도 2가지가 있는데 이벤트 방식과 동기방식(블락)입니다. 이것들을 지원하는 자바의 API는  gorun999 님이 언급하신 AsyncTask , Callable 로 구현할수 있고, 이미 잘구현된CompletableFuture 도 있습니다. CompletableFuture 나 AsyncTask 를 이용하기를 권하고 싶네요.

C#의 경우 이러한 비동기를 구현하기 위해 문법적으로 제공합니다. async await이죠. 그리고 이것은 제가 앞서 말한것과 비슷한 방식으로 구현이 되어있습니다. 미리 생성된 스레드 풀에 분기된 작업을 수행하도록 하고 이벤트를 발생시키거나 동기(블락)를 하여 결과값을 리턴 받습니다. ()


새벽에 써서 그런지 오타 많네요 ㅠ

그리고 한가지 더 이것도 글쓸때는 쓰고 싶었는데 빼먹었네요. 위에서 익명클래스는  기본적으로 this (익명클래스의 this가 아닌 외부클래스의 this 객체)를 포함하고 있다고 하였는데, 그리고 이 얘기는 한가지 문제를 야기합니다. 익명클래스를 사용하는 경우 익명클래스의 객체 생명주기가 외부클래스의 객체 생명주기보다 길어지는 경우 익명클래스의 객체 생명주기가 끝날때까지 외부클래스의 객체가 가비지 컬렉트가 되지 않아서 메모리 누수가 됩니다.

이것은 당연한 이야기 이지만,  익명클래스가 this객체의 참조를 갖을때 명시적으로 갖는 것이 아니고 묵시적으로 갖기 때문에 익명클래스가 this객체를 기본적으로 캡쳐한다는 사실을 모른다면 문제가 되겠죠, 그래서 이런걸 신경쓰지 않기 위해서 익명클래스를 사용할떄 static을 사용하던지 익명클래스를 구현한 새 클래스를 사용하는 방법을 사용합니다. 아니면 최근의 방식인 람다를 사용하죠 람다는 static 과 익명클래스를 구현하는 방법보다 더 세련된 방법을 제공합니다. 이것에 관련된거 여기까지 마무리 하겠습니다. 중요한건 익명클래스가 외부클래스의 this객체를 참조하고 있다는 사실을 알고 있어야 한다는 겁니다.



2018-01-18 09:27:14 에 아래 내용에서 변경 됨 #1

뭐라고 답변을 써야 하나...... 질문이 딱 단답으로 하기에는 오해할까봐 걱정이 되네요..

이걸 어떻게 잘 정리해서 답변할수 있을지. 잘 답변하도록 노력해보겠습니다.


2가지 측면에서 말씀을 드려 보겠습니다. 하나는 문법적 측면 두번쨰는 구현하려는 기능에 특성적 측면입니다. 사실 이 두가지가 앞서 달린 답변에 다 있네요.

첫번째 문법적 측면에서 입니다. 자바의 익명클래스와 C#의 익명함수에 대해서 동일시하게 생각하시면 됩니다. 글쓴이분께서 말씀하신 C#의 문법과 자바의 익명클래스는 그 동작의 거의 비슷합니다. 하지만 한가지가 다릅니다. 다른 글에서도 소개했는데 자바의 익명클래스와 C#의 익명함수는 외부의 지역변수를 캡쳐하는데 이때 이들을(익명클래스, 익명함수) 클로저라고 부릅니다. 그리고 자바와 C#의 클로저는 동작이 서로 조금 다릅니다. 정확히는 자바는 제한적인 클로저를 제공합니다. 그 결과가 익명클래스 내에서 캡쳐한 외부 지역변수는 변경할수 없다는 거죠, C#은 변경이 가능합니다. 자바의 이 변경할수 없다는 사용자가 외부 지역변수의 final키워드를 강요합니다. 그렇지 않으면 컴파일러가 에러를 출력합니다.

(참고로 자바 7? 8? 에서는 실질적으로 final이면 키워드를 쓰지 않아도 컴파일러가 알서 판단합니다.)

문법 소개는 이쯤에서 마치고 자바에서 문법적으로 str을 변경하는 법에 대해서 소개를 해보겠습니다.

외부 지역변수에 대해서 변경할수 없다고 하였는데, 이말은 str을 익명클래스의 외부 지역변수를 멤버로 가진 외부클래스의 멤버 변수로 사용하는 경우 str의 값을 변경할수 있다는 겁니다.

Class Ouster {

    public String str;

    public String method () {

         new Thread(new Runnable() {

              public void run() {    str =  "";           // 변경   가능 }

         }).start();

        return str;

     }

}

아니면 str을 멤버변수로 가지는 다른 클래스를 정의하여 해당 클래스의 객체를 생성해서 이 객체를 캡처하고 해당 객체의 멤버인 str을 변경하는 방법도 있습니다.

Class Ouster {

    public UserClass method () {

         UserClass user = new UserClass();

         new Thread(new Runnable() {

              public void run() {    user.str =  "";           // 변경   가능 }

         }).start();

        return user;

     }

}

사실 첫번째 방법은 두번째 방법이 되기때문에 가능한겁니다. 자바에서 캡쳐된 변수를 변경하지는 못하는 제약이 있지만 해당 변수의 멤버를 변경하지 못하는건 아닙니다. 이것이 두번쨰 방법이고 익명클래스가 캡쳐하는 지역변수는 기본적으로 this를 포함하고 있습니다. 여기서 this는 외부지역변수를 멤버로 가진 외부클래스 객체를 가리키고요 즉 this객체의 멤버인 str은 변경이 가능한거죠(첫번쨰와 두번째가 같다는 말이 어떤 뜻인지 알겠나요?)


두번재 구현하려는 기능적 측면에서 말씀을 드려보겠습니다. 사실 이게 제일 중요한데 제일 설명하기 어렵네요. 코드를 보니 구현하려는 기능의 특성이 비동기를 구현하려는것 같습니다. 코드를 보니 그러려고 했을거라 유추가 되는거죠. 하지만 반대로 비동기에 대한 특성을 잘 이해하고 구현하려고 하시는건지는 의문이 갑니다. 이유는 어설프게 str을 return 하고 있기 때문인데요. 

일단 작성하신 메소드의 내용만 보면 GetWebAPi 메소드는 호출 이후 String을 바로 return하겠죠, 그리고 코드의 실행순서는 바로 쭉 나갈겁니다. 이와 동시에 스레드가 동작하고 str에 내용을 채우겠죠.

즉 str에 내용을 채우는 이 코드과 원래의 코드와 비동기적으로 일어난다는 겁니다. 즉 수행시간이 길어질수 있는 입출력을 수행하는 GetWebAPi 메소드를 호출할때 블락이 되지않도록(즉 해당 코드에서 긴 시간이 소요되어 다음 코드로 넘어 가지 않는) 하는 비동기의 장점을 이용하려는 것처럼 보입니다.

이것은 UserClass result = GetWebAPi()에서 result가 언제 내가 원하는 값으로 바뀔지 알수가 없다는 의미 이기도 하지요. 즉 result.str에 내용을 일정시간마다 확인을 해야겠죠.

비동기 구현 방법은 2가지가 핵심입니다. 수행시간이 길어질수 있는 코드를 분리해 내어 비동기적으로 수행하도록 하는것(위에 스레드를 사용한것이겠죠) 과 그렇게 비동기적으로 수행된 결과값이 있는 경우 또는 비동기적으루 수행된 코드의 완료 여부를 받는것 입니다.(또는 중간 중간 상태 또는 중간 결과를 받는것도 포함되죠). 제가 볼떄 글쓴이 분께서 원하시는건 비동기적으로 수행된 코드가 완료된 시점에 결과를 받는것이 아닐까 생각이 드네요. 그리고 해당 코드에서는 이러한 부분이 어설프고요

또한 작성하신 코드는 비동기를 구현하기 위해 즉 분기를 주기 위해 스레드를 생성하고 있는데, 이것도 성능상 바르지 못합니다. GetWebAPi메소드가 호출되면서 스레드를 생성하는데, 이것보단 미리 생성된 스레드를 이용하는 것이 더 좋습니다.

완료된 결과를 리턴 받는 방법도 2가지가 있는데 이벤트 방식과 동기방식(블락)입니다. 이것들을 지원하는 자바의 API는  gorun999 님이 언급하신 AsyncTask , Callable 로 구현할수 있고, 이미 잘구현된CompletableFuture 도 있습니다. CompletableFuture 나 AsyncTask 를 이용하기를 권하고 싶네요.

C#의 경우 이러한 비동기를 구현하기 위해 문법적으로 제공합니다. async await이죠. 그리고 이것은 제가 앞서 말한것과 비슷한 방식으로 구현이 되어있습니다. 미리 생성된 스레드 풀에 분기된 작업을 수행하도록 하고 이벤트를 발생시키거나 동기(블락)를 하여 결과값을 리턴 받습니다.