티스토리 뷰

개인공부/JAVA

JAVA - Inner Class, Lambda , Stream

날따라해봐요요롷게 2021. 3. 21. 01:10

내부 클래스, 람다식, 스트림

  • 내부 클래스
  • 람다식
  • 스트림

  • 내부 클래스

: 내부 클래스는 클래스 내부에 선언한 클래스를 말한다. 클래스 내부에 클래스를 사용하는 이유는 외부 클래스와 관련이 있던가, 다른 클래스와는 관련되지 않아 사용 하거나, 될 일이 없기 때문입니다.

내부 클래스는 선언하는 위치나 예약어에 따라 네 가지 유형을 갖는다.

  • 인스턴스 내부 클래스
  • 정적 내부 클래스
  • 지역 내부 클래스
  • 익명 내부 클래스

## 인스턴스 내부 클래스

: 인스턴스 변수를 선언 할 때와 같이 선언 / 외부 클래스 내부에서만 생성하여 사용하는 객체를 선언할 때 사용

특징 : 외부 클래스 생성 후 인스턴스 내부 클래스가 생성된다.

class OutClass{ //외부 클래스
    private int num = 10;    //외부 클래스 private 변수
    private static int sNum = 20; // 외부 클래스 정적 변수

    private InClass inClass; //내부 클래스 자료형 변수 선언

    public OutClass() { //외부 클래스 디폴트 생성자
        inClass = new InClass(); // 외부클래스 생성된 후에 내부 클래스 생성
    }

    class InClass {    //인스턴스 내부 클래스
        int inNum = 100;    //내부 클래스의 인스턴스 변수
        /*static int sInNuM = 200;
        static 정적변수는 컴파일을 하면서 메모리에 올라간다.
        우리가 선언한 변수는 내부 클래스 변수로서 외부클래스가 생성이되어야 생성이 될 수 있다. 따라서 내부 클래스의 정적 변순는 선언될 수 없다.
        */ 
        void inTest() { // 내부클래스 - 메서드
            System.out.println("outClass num = " + num + " - 외부클래스의 인스턴스 변수");
            System.out.println("outClass sNum = " + sNum + " - 외부클래스의 정적 변수");
        }
    }

    public void usingClass() { // 내부클래스  - 메서드
        inClass.inTest();
    }
}

public class InnerTest {

    public static void main(String[] args) {
        OutClass outClass = new OutClass();
        System.out.println("외부 클래스 이용하여 내부 클래스 기능 호출");
        outClass.usingClass(); // 내부클래스 메서드 사용

    }
}

코드보기

: 외부 클래스 선언 후 외부 클래스 변수를 선언한다. private, static 변수 두 개를 선언.

내부 클래스 자료형 변수를 선언 -> 외부 클래스 디폴트 생성자를 통해서 외부 클래스 생성 후 내부 클래스가 생성되게 코드작성.

인스턴스 내부 클래스 작성 -> 변수와 메서드 생성.

메인함수 : 외부클래스를 생성 외부클래스 참조 변수를 활용하여 내부 클래스 메서드 호출.

코드 특징!

- 외부 클래스의 변수는 private으로 선언 되었지만, 내부 클래스에서 사용 가능

- 내부 클래스에 정적(static) 변수 선언 안됨

(정적 변수는 클래스 생성과 상관 없이 static 메모리 영역에 올라간다. 하지만 인스턴스 내부 클래스는 외부 클래스가 생성이 된 후 생성이 가능하다. 따라서 인스턴스 내부 클래스에는 정적 변수를 선언 할 수 없다.)

## 정적 내부 클래스 (static)

: 내부 클래스가 외부 클래스의 생성과 상관없이 사용할 수 있도록 하기 위해서는 정적 내부 클래스를 사용한다.

class OutClass1{ //외부 클래스
    private int num = 10;    //외부 클래스 private 변수
    private static int sNum = 20; // 외부 클래스 정적 변수

    static class InStaticClass{
        int inNum = 100;
        static int sInNum = 200;

        void inTest() {
            System.out.println("InStackClass inNum = " + inNum + " - 내부 클래스의 인스턴스 변수");
            System.out.println("InStackClass sInNum = " + sInNum + " - 내부 클래스의 정적 변수");
            System.out.println("OutClass sNum = " + sNum + " - 외부 클래스의 정적 변수");

        }
        static void sTest() {
            System.out.println("OutClass Num = " + sNum + " - 외부 클래스의 정적 변수 사용");
            System.out.println("InStackClass sInNum = " + sInNum + " - 내부 클래스의 정적 변수");
        }
    }
}

public class InnerTest1 {

    public static void main(String[] args) {
//        OutClass outClass = new OutClass();
        OutClass1.InStaticClass sInClass = new OutClass1.InStaticClass();
        System.out.println("정적 내부 클래스 일반 메서드 호출");
        sInClass.inTest();
    }
}

코드보기 :

static class InStaticClass 정적 내부 클래스를 선언 -> 인스턴스, 정적 변수 선언

inTest() 일반 메서드 생성 -> 외부 클래스의 인스턴스 변수는 사용 할 수 없다.

sTest() 정적 메서드 생성 -> 외부, 내부 클래스의 인스턴스 변수 사용 할 수 없다.

OutClass1.InStaticClass sInClass = new OutClass1.InStackClass();

-> 정적 내부 클래스는 외부 클래스를 생성하지 않고도 내부 클래스 자료형으로 선언하여 생성이 가능하다.

sInClass.inTest(); -> 내부 클래스 생성 후 참조변수로 메서드 호출

OutClass1.InStaticClass.sTest(); -> 정적 내부 클래스에 선언한 메서드나 변수가 private이 아닌경우 다른 클래스에서도 사용 가능

정적 내부 클래스 메서드 변수 유형 사용 가능 여부
일반 메서드 외부 클래스의 인스턴스 변수 X
외부 클래스의 정적 변수 O
정적 내부 클래스의 인스턴스 변수 O
정적 내부 클래스의 정적 변수 O
정적 메서드 외부 클래스의 인스턴스 변수 X
외부 클래스의 정적 변수 O
정적 내부 클래스의 인스턴스 변수 X
정적 내부 클래스의 정적 변수 O

위의 표를 통해서 Static 클래스, 메서드, 변수가 어떻게 쓰이는지 그리고 컴파일 과정에서 메모리에 어떻게 어디에 적재가 되는지 알고 이해를 해야한다.

## 지역 내부 클래스

: 지역 변수 처럼 메서드 내부에 클래스를 정의하는 것 -> 메서드 안에서만 사용이 가능하다.

class Outer {
    int outNum = 100;
    static int sNum = 200;

    Runnable getRunnable(int i) {
        int num = 100;

        class MyRunnable implements Runnable {
            int localNum =10;

            @Override
            public void run() {
                // num = 200;
                // i = 100;
                System.out.println("i =" + i);
                System.out.println("num = "+ num);
                System.out.println("localNum = " + localNum);
                System.out.println("outNum = " + outNum);
                System.out.println("Outer.sNum = " + Outer.sNum);
            }
        }
        return new MyRunnable();
    }
}

public class LocalInnerTest {
    public static void main(String[] args) {
        Outer out = new Outer();
        Runnable runner = out.getRunnable(10);
        runner.run();
    }
}

코드보기!

: Runnable 인터페이스를 구현하는 클래스를 지역 내부 클래스로 만듦!

getRunnable 메서드 선언 해당 메서드는 반환형이 Runnable 이다. -> 해당 메서드는 Runnable 자료형의 객체를 생성하여 반환해야한다. MyRunnable 지역 내부 클래스 선언. return new MyRunnable(); -> MyRunnable 클래스를 생성한 후 반환. 외부 클래스 out 객체 생성.

Runnable runner = out.getRunnalbe(10); -> Runnable형 객체로 getRunnable()을 호출한다.

MyRunnable을 사용하려면 클래스를 직접 생성하는 것이 아닌 getRunnable() 메서드 호출을 통해 생성된 객체를 반환받아야한다.

## 익명 내부 클래스

:

  • 람다식
  • : 함수의 구현과 호출 만으로 프로그램을 만드는 방식 -> "함수형 인터페이스"

(매개변수) -> { 실행문; }

int add(int x, int y) { return x + y ; }

(int x, int y) ->{return x+y;}

람다식 문법!

- 매개변수 자료형과 괄호 생략하기

: 매개변수 자료형을 생략할 수 있다, 매개변수가 하나인 경우 괄호 생략 가능

str -> {System.out.println("str");}

매개변수가 2개 이상인 경우 괄호 생략x

- 중괄호 생략 : 중괄호의 구현 부분이 한 문장인 경우 중괄호 생략 가능

str -> {System.out.println("str");} => str -> System.out.println(str);

-- return 생략 : 중괄호 안의 구현 부분이 return문 하나라면 중괄호와 return 모두 생략 가능

(x,y) -> x+y

str -> return str.length()

람다식 사용

: 람다식을 구현하기 위해서는 먼저 인터페이스를 만들고 인터페이스에 람다식으로 구현할 머세드를 선언한다.

"함수형 인터페이스"

package lambda;

public interface MyNumber {
    int getMax(int num1, int num2);
}

=====

package lambda;

public class TestMyNumber {
    public static void main(String[] args) {
        MyNumber max = (x,y) -> (x >=y) ? x : y;
        System.out.println(max.getMax(10, 23));
    }
}

코드보기!
인터페이스 MyNumber를 만들어 getMax 메서드를 선언한다.

메인함수에서 MyNumber 인터페이스형 변수를 max를 선언하고 변수에 람다식을 대입한다.

변수 자료형이 MyNumber 이므로 max 인터페이스 변수를 활용하여 getMax 메서드를 호출할 수 있다.

 

## 함수형 인터페이스

 : 람다식은 메서드 이름이 없고 메서드를 실행하는 데 필요한 매개변수를 활용한 실행코드를 구현하는 것이다.

그렇다면 메서드를 어디에 선언하고 구현을 해야하는가?

함수형 언어에서는 함수만 따로 호출 가능하다. 하지만 자바에서는 참조 변수 없이는 메서드를 호출할 수 없다.

그렇기에!! 람다식을 구현하기 위해 함수형 인터페이스를 만들고, 인터페이스에 람다식으로 구현할 메서드를 선언한다.

람다식은 하나의 메서드를 구현하여 인터페이스형 변수에 대입하기에 인터페이스가 두 개 이상의 메서드를 가질 수 없다.

 

함수형 인터페이스 임을 알려주기 위해 : @FunctionalInterface 애노테이션을 사용

package lambda;

public interface StringConcat {
	public void makeString(String s1, String s2);
}
======
package lambda;

public class StringConCatImpl implements StringConcat {

	@Override
	public void makeString(String s1, String s2) {
		System.out.println(s1 + s2);
	}
}
=====
public class TestStringConcat {

	public static void main(String[] args) {
		String s1 = "Hello";
		String s2 = "World";
		StringConcat concat1 = new StringConCatImpl();
		concat1.makeString(s1, s2);
		StringConcat concat2 = (s,v) -> System.out.println(s + "," + v); //함수 선언과 동시에 생성! 기능을 변수에 대입
		concat2.makeString(s1, s2);
	}

}

 

StringConcat인터페이스는 makeString() 메서드를 가지고 있다. 

일반 클래스로 구현하기 위해서는 인터페이스에서 정의되지 않은 메서드를 override 해준다.

그와 달리 람다식은 main 함수에서 StringConcat 인터페이스 자료형의 concat2 변수를 생성하여 람다식을 대입한다.

인터페이스 자료형으로 선언되어 makeString 메서드 사용가능 하기에 메서드를 호출하여 사용할 수 있다.

 

## 익명 객체를 생성하는 람다식 --> 어려움..

 : 익명 내부 클래스는 클래스 이름 없이 자료형 변수에 바로 메서드 구현부를 생성하여 대입할 수 있다.

람다식으로 구현을 하면 컴퓨터 내부에서는 익명클래스가 생성되고 이를 통해 익명 객체가 생성된다.

 

## 함수를 변수처럼 사용하는 람다식

 : 변수를 사용하는 세 가지 

- 자료형으로 선언 후 값 대입 : int x = 20;

- 매개변수로 전달 : int add(int x, int y);

- 메서드의 반환 값으로 반환 : return num;

 

매개변수로 전달하는 람다식 ->> ???? 어려움 다시 공부!!

interface PrintString {
	void showString(String str);
}
public class TestLambda {
	public static void main(String[] args) {
		PrintString print1 = s -> System.out.println(s);
		print1.showString("come on!!");
		showMyString(print1);
	}
	public static void showMyString(PrintString p) {
		p.showString("인터페이스형 매개변수");
	}
}

클래스에 정적 메서드 추가 showMyString 메서드를 호출할 때 람다식을 대입한 print1을 매개변수로 전달

매개변수의 자료형은 PrintString 이고 변수는 p

 

반환값으로 쓰이는 람다식

public static PrintString returnString(){
	PrintString str = s -> System.out.println(s + "world");
    return str;
}

======
public static PrintString returnString() {
	return s->System.out.println(s + "world");
}

메서드의 반환형을 PrintString 인터페이스형으로 선언하면 람다식을 반환할 수 있다.

 

  • 스트림

 

 

 

출처 : DO IT! 자바프로그래밍 (저 : 박은종)

'개인공부 > JAVA' 카테고리의 다른 글

Exception  (0) 2021.09.09
배열, 기본클래스  (0) 2021.05.05
JAVA - CLASS  (0) 2021.03.16
JAVA - INTERFACE  (0) 2021.03.12
JAVA - Abstract  (0) 2021.03.10
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
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
글 보관함