반응형
1. 여러 내부 클래스의 정의와 유형
내부 클래스란? ( inner class )
- 클래스 내부에서 선언한 클래스로 이 클래스를 감싸고 있는 외부 클래스와 밀접한 연관이 있는 경우가 많고,
다른 외부 클래스에서 사용할 일이 거의 없는 경우에 내부 클래스로 선언해서 사용함 - 중첩 클래스라고도 함
내부 클래스의 종류
- 인스턴스 내부 클래스
- 정적(static) 내부 클래스
- 지역(local) 내부 클래스
- 익명(anonymous) 내부 클래스
인스턴스 내부 클래스
- 내부적으로 사용할 클래스를 선언 ( private로 선언하는 것을 권장 )
- 외부 클래스가 생성된 후 생성됨 ( 정적 내부 클래스와 다름 )
- private가 아닌 내부 클래스는 다른 외부 클래스에서 생성이 가능하다
OutClass outClass = new OutClass();
OutClass.InClass inClass = outClass.new InClass();
인스턴스 내부 클래스 예
class OutClass {
private int num = 10;
private static int sNum = 20;
private InClass inClass;
public OutClass(){
inClass = new InClass(); // 내부 클래스 생성
}
class InClass{
int inNum = 100;
//static int sInNum = 200; //에러 남
void inTest(){
System.out.println("OutClass num = " +num + "(외부 클래스의 인스턴스 변수)");
System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수)");
System.out.println("InClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수)");
}
//static void sTest(){ //에러 남
//}
}
public void usingClass(){
inClass.inTest(); //내부 클래스 변수를 사용하여 메서드 호출하기
}
}
public class InnerTest {
public static void main(String[] args) {
OutClass outClass = new OutClass();
System.out.println("외부 클래스 이용하여 내부 클래스 기능 호출");
outClass.usingClass(); // 내부 클래스 기능 호출
System.out.println();
OutClass.InClass inClass = outClass.new InClass(); // 외부 클래스를 이용하여 내부 클래스 생성
System.out.println("외부 클래스 변수를 이용하여 내부 클래스 생성");
inClass.inTest();
}
}
정적 내부 클래스
- 외부 클래스 생성과 무관하게 사용할 수 있음
- 정적 변수, 정적 메서드 사용
정적 내부 클래스 예
class OutClass {
private int num = 10;
private static int sNum = 20;
private InClass inClass;
public OutClass(){
inClass = new InClass(); // 내부 클래스 생성
}
class InClass{
int inNum = 100;
//static int sInNum = 200; //에러 남
void inTest(){
System.out.println("OutClass num = " +num + "(외부 클래스의 인스턴스 변수)");
System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수)");
System.out.println("InClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수)");
}
//static void sTest(){ //에러 남
//}
}
public void usingClass(){
inClass.inTest(); //내부 클래스 변수를 사용하여 메서드 호출하기
}
static class InStaticClass{
int inNum = 100;
static int sInNum = 200;
void inTest(){ //정적 클래스의 일반 메서드
//num += 10; // 외부 클래스의 인스턴스 변수는 사용할 수 없음.
System.out.println("InStaticClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수 사용)");
System.out.println("InStaticClass sInNum = " + sInNum + "(내부 클래스의 스태틱 변수 사용)");
System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수 사용)");
}
static void sTest(){ // 정적 클래스의 static 메서드
//num += 10; // 외부 클래스의 인스턴스 변수는 사용할 수 없음.
//inNum += 10; // 내부 클래스의 인스턴스 변수는 사용할 수 없음
System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수 사용)");
System.out.println("InStaticClass sInNum = " + sInNum + "(내부 클래스의 스태틱 변수 사용)");
}
}
}
public class InnerTest {
public static void main(String[] args) {
.....
//외부 클래스 생성하지 않고 바로 정적 내부 클래스 생성
OutClass.InStaticClass sInClass = new OutClass.InStaticClass();
System.out.println("정적 내부 클래스 일반 메서드 호출");
sInClass.inTest();
System.out.println();
System.out.println("정적 내부 클래스의 스태틱 메소드 호출");
OutClass.InStaticClass.sTest();
}
}

지역 내부 클래스
- 지역 변수와 같이 메서드 내부에서 정의하여 사용하는 클래스
- 메서드의 호출이 끝나면 메서드에 사용된 지역변수의 유효성은 사라짐
- 메서드 호출 이후에도 사용해야 하는 경우가 있을 수 있으므로 지역 내부 클래스에서 사용하는
메서드의 지역 변수나 매개 변수는 final로 선언됨
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("Outter.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();
}
}
- MyRunnable 클래스를 사용하려면 직접 생성하는 것이 아닌 getRunnable()메서드를 호출하여
생성된 개체를 반환 받아야 한다.
익명 내부 클래스
- 이름이 없는 클래스 ( 위 지역 내부 클래스의 MyRunnable 클래스 이름은 실제로 호출되는 경우가 없다 )
- 클래스의 이름을 생략하고 주로 하나의 인터페이스나 하나의 추상 클래스를 구현하여 반환
- 인터페이스나 추상 클래스 자료형의 변수에 직접 대입하여 클래스를 생성하거나
지역 내부 클래스의 메서드 내부에서 생성하여 반환 할 수 있음 - widget의 이벤트 핸들러에 활용됨

익명 내부클래스의 예
class Outter2{
Runnable getRunnable(int i){
int num = 100;
return new Runnable() {
@Override
public void run() {
//num = 200; //에러 남
//i = 10; //에러 남
System.out.println(i);
System.out.println(num);
}
};
}
Runnable runner = new Runnable() {
@Override
public void run() {
System.out.println("Runnable 이 구현된 익명 클래스 변수");
}
};
}
public class AnonymousInnerTest {
public static void main(String[] args) {
Outter2 out = new Outter2();
Runnable runnerble = out.getRunnable(10);
runnerble.run();
out.runner.run();
}
}
2. 람다식 ( Lambda expression )
함수형 프로그래밍과 람다식
- 자바는 객체지향 프로그래밍 기능을 수행하기 위해선 객체를 만들고 그 객체 내부에 멤버변수를 선언하고 기능을 수행하는 메서드를 구현
- 자바8부터 함수형 프로그래밍 방식을 지원하고 이를 람다식이라고 함.
- 함수의 구현과 호출만으로 프로그래밍이 수행되는 방식
- 함수형 프로그래밍 ( Functional Programming )
함수형 프로그래밍은 순수함수(pure function)를 구현하고 호출함으로써 외부 자료에 부수적인 영향(side effect)를 주지 않도록 구현하는 방식입니다. 순수 함수란 매개변수만을 사용하여 만드는 함수 입니다. 즉, 함수 내부에서 함수 외부에 있는 변수를 사용하지 않아 함수가 수행되더라도 외부에는 영향을 주지 않습니다.
함수를 기반으로 하는 프로그래밍이고 입력받는 자료 이외에 외부 자료를 사용하지 않아 여려 자료가 동시에 수행되는 병렬처리가 가능합니다. 함수형 프로그래밍은 함수의 기능이 자료에 독립적임을 보장합니다. 이는 동일한 자료에 대해 동일한 결과를 보장하고, 다양한 자료에 대해 같은 기능을 수행할 수 있습니다.
람다식 문법
- 익명 함수 만들기
- 매개 변수와 매개변수를 이용한 실행문 ( 매개변수 ) -> { 실행문 };
두 수를 입력받아 더하는 add() 함수의 예
int add(int x, int y){
return x + y;
}
람다식으로 표현
(int x, int y) -> {return x+y;}
매개 변수가 하나인 경우 자료형과 괄호 생략가능
str -> {System.out.println(str)};
매개 변수가 두개이상인 경우 괄호를 생략할 수 없음
x, y -> {System.out.println(x+y);} // 오류
실행문이 한 문장이라도 return문은 중괄호를 생략할 수 없음
str-> return str.length();
실행문이 한 문장의 반환문인 경우엔 return과 중괄호를 모두 생략
(x, y) -> x+y;
str -> str.length;
( Interface ) Add.java
public interface Add {
int add(int x, int y);
}
( class ) AddTest.java
public class AddTest {
public static void main(String[] args) {
Add add = (x, y)-> {return x+y;};
System.out.println(add.add(10, 20)); // 30
}
}
3. 함수형 인터페이스와 람다식 구현하여 사용하기
함수형 인터페이스 선언하기
- 람다식을 선언하기 위한 인터페이스
- 익명 함수와 매개변수 만으로 구현되므로 인터페이스는 단 하나의 메서드만을 선언해야함
- @FunctionalInterface 에노테이션( annotation )
함수형 인터페이스라는 의미, 내부에 여러개의 메서드를 선언하면 에러 발생


람다식 구현과 호출


4. 객체지향 프로그래밍 VS 람다식 구현
객체지향 프로그래밍과 람다식 비교
- 문자열 두 개를 연결하여 출력하는 예제를 두 가지 방식으로 구현해본다
( Interface ) StringConcat.java
public interface StringConcat {
public void makeString(String str1, String str2);
}
( class ) StringConcatImpl.java
public class StringConcatImpl implements StringConcat {
@Override
public void makeString(String str1, String str2) {
System.out.println(str1 + "," + str2);
}
}
StringConcatTest.java
public class StringConcatTest {
public static void main(String[] args) {
StringConcatImpl strImpl = new StringConcatImpl();
strImpl.makeString("가나다", "라마바"); // 가나다,라마바
}
}
람다식으로 구현
String s1 = "가나다";
String s2 = "라마바";
StringConcat concat = (s, v)->System.out.println(s + "," + v);
concat.makeString(s1, s2);
익명 객체를 생성하는 람다식
- 자바에서는 객체 없이 메서드가 호출될 수 없음
- 람다식을 구현하면 익명 내부 클래스가 만들어지고, 이를 통해 익명 객체가 생성됨
StringConcat concat2 = new StringConcat() {
@Override
public void makeString(String str1, String str2) {
System.out.println(str1 + "..." + str2);
}
};
concat2.makeString(s1, s2);
- 익명 내부 클래스에서와 마찬가지로 람다식 내부에서 에서도 외부에 있는 지역 변수의 값을 변경하면 오류가 발생함

함수를 변수처럼 사용하는 람다식
특정 자료형으로 변수를 선언 한 후 값을 대입함 int a = 10;
매개 변수로 전달하여 사용함 int add(int x, int y)
메서드의 반환 값으로 반환 하기 return num;
인터페이스형 변수에 람다식 대입하기
함수형 인터페이스
interface PrintString{
void showString(String str);
}
PrintString lambdaStr = s->System.out.println(s); //람다식을 변수에 대입
lambdaStr.showString("hello lambda_1");
- 매개변수로 전달하는 람다식
showMyString(lambdaStr);
public static void showMyString(PrintString p) {
p.showString("hello lambda_2");
}
- 반환 값으로 쓰이는 람다식
public static PrintString returnString() { //반환 값으로 사용
return s->System.out.println(s + "world");
}
PrintString reStr = returnString();
reStr.showString("hello ");
반응형
'Java' 카테고리의 다른 글
| 자바의 다양한 기능 / 입출력 스트림, 직렬화, 입출력 클래스 (0) | 2022.12.09 |
|---|---|
| 자바의 다양한 기능 / 스트림, 예외처리 (0) | 2022.12.07 |
| 자바와 자료구조 / Generic, T extends, 컬렉션 프레임워크 (0) | 2022.11.30 |
| 자바와 자료구조 / 배열, 스택, 큐 (0) | 2022.11.29 |
| 자바의 유용한 클래스 / Object, String, StringBuilder, StringBuffer, textblock, Class 클래스 (0) | 2022.11.22 |