쪼꼬렛
231
2019-10-04 01:33:09 작성 2019-10-04 01:52:47 수정됨
6
523

람다와 익명 구현 객체의 this가 가리키는것 차이



public class UsingThis {
    public int outterField = 10;

    class Inner {
        int innerField = 10;
        int outterField = 30;

        void method() {

            int innerField =30 ;
            // 람다식
            MyFuntionalInterface fi = () -> {
                // int innerField = 40; // 오류 발생 -> "이미 갖고있는 변수이다"라는 메세지
                System.out.println("outterField: " + outterField);
                System.out.println("UsingThis.this.outterField: " + UsingThis.this.outterField + "\n");

                System.out.println("innerField: " + innerField);
                System.out.println("this.innerField: " + this.innerField);
            };
            fi.method();

            fi = new MyFuntionalInterface() {
                int innerField = 40;
                @Override
                public void method() {
                    System.out.println(innerField);
                    System.out.println("this.innerField: " + this.innerField);

                }
            };
            fi.method();
        }
    }


}
package ramdatest;

public class UsingThisExample {
    public static void main(String[] args) {
        UsingThis usingThis = new UsingThis();
        UsingThis.Inner inner = usingThis.new Inner();
        inner.method();
    }
}


실행결과 : 


outterField: 30
UsingThis.this.outterField: 10

innerField: 30
this.innerField: 10
40
this.innerField: 40

Process finished with exit code 0




여기서 유추할수있는내용은 


인터페이스와 람다식이 가리키는 this가 서로 다르다는겁니다. 


제가 실행해본 결과를 토대로 보면 


익명 구현 객체(== 익명 자식객체도 동일한 상황) 는 this가 가리키는 객체가 자신을 소환한 참조변수 타입입니다.

부모격 객체인 MyFunctionalInterface fi 객체입니다.


람다식은 다릅니다. 


람다식은 자신을 소환한 인터페이스 가아니고 자신이 속한 현재 클래스를 객체로 지목합니다.. 

Inner 클래스가 객체로 지목되죠 




람다랑 익명 객체랑은 서로 비슷할거라 생각했었습니다. 

왜냐면 책에서 "람다식의 형태는 매개 변수를 가진 코드블록이지만, 런타임 시에는 익명 구현 객체를 생성한다."고 적혀있기 때문입니다. 


그럼에도 람다는 차이가있다면 위와같은차이도 있고 한가지 더 차이가 존재합니다. 

바로 람다식을 불러들이는 인터페이스는 단 1개만 추상메소드를 가질 수 있다는 점입니다.  그로인해서 람다는 자동으로 추상메소드에대한 오버라이딩 코드블록을 제공하며 문법상 유추가가능하니 대부분 생략한 문법을 제공합니다.


MyFunctionalInterface fi = x -> System.out.println(x); 
fi.method();


코드량이 많이 줄더라고요 ..원래대로 익명객체로 작성하게되면


MyFunctionalInterface fi = new MyFunctionalInterface () { 
    @Override 
    public void method( int x ) { 
    System.out.println(x);
    } 
}; 
fi.method();


제가 해보니 default 메소드와 static 메소드는 존재해도 상관이 없었지만 attrubute(단어가 스펠링이 맞나..추상 ) 메소드는 한개만 가져야만 람디식을 사용할 수 있습니다.


이때문에 함수적 인터페이스라고 하는걸지도 모른다는 생각이 들었습니다. 


익명 객체는 코드블록 안에 구현 클래스처럼 여러개의 메소드나 필드를  작성해도 상관이없었는데(생성자는 못쓰지만) 

람다는 단하나의 함수를 작성하는 느낌이더라고요.. 




여기서 질문드리고 싶은건 


익명 객체는 왜 this가 자신의 부모타입객체를 지목하는걸까

그리고 람다는 왜 this가 자신을 불러들이는 클래스를 객체로 지목하는걸까. 

이 둘은 왜 서로 다른 this가 되나?? 


익명 객체는 어느정도 유추가됩니다. 익명 객체는 익명이기때문에 이름이없어서이다. 이름이없는 자신을 지목할수가없어서 바로 윗단계인 부모객체를 가리킨다. 


그런데 람다는 제생각와 다르고  뜻밖이었습니다. 람다도 익명이기때문에 지목할 대상을 찾아야하고 바로 윗단계인 인터페이스 타입을 지목할거라생각했는데  제 오산이었습니다. 그 위인 클래스를 지목하더라고요.

익명 객체와 같은 this가 아니라니 햇갈려지는 부분이기도 합니다. 



람다에대해 자세히 공부하신 선배분이 최대한 쉽게 알려주신다면 많은 도움이 될 것 같습니다.

0
0
  • 답변 6

  • jslovers
    2k
    2019-10-04 02:11:42

     람다와 익명의 this 동일하네요

    0
  • 쪼꼬렛
    231
    2019-10-04 02:22:46 작성 2019-10-04 02:23:41 수정됨

    오 한번 다시해보겠습니다! 같다니..! 그런데 책에있는 설명을 제대로 이해못한것갘네요 흠.. 책한번 더 보고 그래도 동일하다고 생각하면 재질문 한번 드려보겠습니다!

    0
  • 쪼꼬렛
    231
    2019-10-04 02:25:29 작성 2019-10-04 02:30:13 수정됨

    혹시 위 예제코드대로 실행하셨나요?

    신용권님 책에있는 예제코드를 조금만 변형해본것입니다


    아직도 잘모르겠네요

    람다식안에 int innerField 선언해보면 이미있는 변수라고나옵니다 즉 람다는 바깥 클래스 변수와 같은 페이지상으로 인식한다는 것이고


    반면에


    익명 객체는 코드안에 int innerField 선언을 해도 오류가 안나오는걸로봐서 바깥과는 다른 공간이라는 것을 알 수 있어서요



    이렇게 생각하면 어떻게 바라보시나요?

    람다와 익명객체는 각각 this가 가리키는게 다른것같은데


    이것을 이해하면 도움이될듯 합니다. 


    0
  • 쪼꼬렛
    231
    2019-10-04 03:15:08 작성 2019-10-04 03:17:18 수정됨

    public class OutClass {
        String field = "OutField";
    
    
        class InnerClass {
            String field = "InnerField";
    
    
            public void InnerClassMethod() {
    
                MyFunctionalInterface fi;
    
                fi = new MyFunctionalInterface() {
                    String field = "AnonymousObjectField";
    
                    @Override
                    public void method() {
                        System.out.println("익명 구현 객체");
                        System.out.println("field: " + field);
                        System.out.println("this.field " + this.field);
                        System.out.println("InnerClass.this.field: "+InnerClass.this.field);
                        System.out.println("OutClass.this.field: " + OutClass.this.field);
                    }
                };
                fi.method();
    
    
                System.out.println();
                fi = () -> {
                    String field = "RamdaField";
                    System.out.println("람다식");
                    System.out.println("field: " + field);
                    System.out.println("this.field: " + this.field);
                    System.out.println("OutClass.this.field: " + OutClass.this.field);
                };
                fi.method();
            }
    
        }
    }


    실행결과 

    익명 구현 객체
    field: AnonymousObjectField
    this.field AnonymousObjectField
    InnerClass.this.field: InnerField
    OutClass.this.field: OutField
    
    람다식
    field: RamdaField
    this.field: InnerField
    OutClass.this.field: OutField
    




    이렇게하니 명확해졌네요 ... 


    람다식의 코드부는 메소드이고  익명구현객체의 코드부는 클래스파일과 같다. 


    람다식의 코드부가 메소드이기때문에 람다식에서 this를 사용할경우, 메소드의 바깥의 객체를 찾게된다. 

    이 this가 가리키는것은 람다식의 인터페이스가 되지 못한다. 왜냐하면 람다식에서 this는 내부적으로 생성되는 익명객체의 참조가 될수 없기때문인데, 더 쉽게말하면 메소드 바깥에 쓸 필드자체를 못만드니 참조를 할것도없는것이다. 따라서 그 윗단계인 바깥클래스를 자동으로 this가 참조하게된다. 





    0
  • jjmean2
    282
    2019-10-04 07:10:24
    0
  • jslovers
    2k
    2019-10-04 12:21:26

    쪼꼬렛 동일하게 메소드 안에서 this를 출력해보세요

    class Inner {
    void method() {
    MyFuntionalInterface fi = () -> {
    System.out.println(this);
    };
    fi.method();

    fi = new MyFuntionalInterface() {
    @Override
    public void method() {
    System.out.println(this);
    }
    };
    fi.method();
    }
    }
    0
  • 로그인을 하시면 답변을 등록할 수 있습니다.