클래스 내부의 클래스 ( Nested Class )
클래스 내부에 클래스를 또 선언할 수 있으며, Nested Class(중첩 클래스)
라고 부름
Nested Class는 두 가지로 갈리는데,
안쪽 클래스가 일반 클래스 라면 Inner Class
라고 부르며
안쪽 클래스가 Static 클래스 라면 Static Nested Class
라고 부른다.
Outer Class
와 다른 점은 Inner Class
는 접근제어자를 4가지(public, private, protected,(없음)) 사용 가능하다는 것.
class OuterClass{
class InnerClass{}
static class StaticNestedClass{}
}
Inner Class
static이 아닌 클래스로, 바깥쪽 클래스의 인스턴스 변수/메소드, 스태틱 변수/메소드에 접근이 가능하다
사용 시에 아우터 클래스의 인스턴스가 있어야 하며, 아우터 클래스의 인스턴스 하위로 들어감.(인스턴스 변수처럼)
class OuterClass{
private int a;
private static int b;
class InnerClass{
public void method(){ // #1 일반 메소드
a = 1; // ✅ 인스턴스 변수 접근 가능
b = 2; // ✅ 스태틱 변수 접근 가능
}
public static void staticMethod(){} // ❌ 선언 불가
/* 스태틱은 클래스에 종속되어야함.
근데? 이너클래스는 아우터클래스의 인스턴스가 있어야 쓸 수 있음
-> 클래스에 종속이 아니라 인스턴스에 종속
❌ 선언 불가
*/
}
}
// 만약 이걸 호출하려면?
OuterClass out = new OuterClass();
OuterClass.InnerClass instance = outer.new InnerClass();
//선언시에 반드시 OuterClass의 인스턴스가 있어야 함.
//outer.new InnerClass(); 문법에 유의
instance.method()
Static Nested Class
static으로 선언된 클래스임. 바깥쪽 스태틱 변수/메소드 에는 접근이 가능하지만, 인스턴스 변수/메소드 에는 접근이 불가능하다.
class OuterClass{
private int a;
private static int b;
static class StaticNestedClass{
public void method(){ // #1 일반 메소드
a = 1; // ❌ 인스턴스 변수 접근 불가
b = 2; // ✅ 스태틱 변수 접근 가능
}
public static void staticMethod(){} // #2 스태틱 메소드
}
}
// 만약 이걸 호출하려면?
//#1 일반 메소드
OuterClass.StaticNestedClass instance = new OuterClass.StaticNestedClass();
//안쪽 클래스가 스태틱이라 OuterClass.StaticNestedClass로 부를 수 있음.
instance.method()
//하지만 스태틱 메소드가 아니라 인스턴스 메소드이기 때문에 instance 만들고 instance.method()로 접근해야함.
//#2 스태틱 메소드
OuterClass.StaticNestedClass.staticMethod();
//안쪽 클래스도 스태틱, 메소드도 스태틱이라 가능.
아니 이걸 왜씀?
- 논리적으로 관련된 클래스를 묶을 수 있음
- 캡슐화 강화... -> 뭔 말임?
- 내부 클래스는 외부 클래스의 private에 접근 가능하니까 외부 클래스 구조를 노출시키기 싫으면 내부 클래스만 호출해서 외부 private 값을 수정하도록 할 수 있음
예시)외부 클래스의 변수는 숨기면서 내부 클래스를 불러서 값은 수정할 수 있음. public class Hidden(){ private int hiddenNum; // <-- 숨기고자 하는 변수 public class InnerClass{ // 외부에서 private 접근을 위한 이너 클래스 public void innerMethod(){ hiddenNum += 1; // <-- 이너 클래스는 외부의 private에 접근 가능 } } }
- 내부 클래스는 외부 클래스의 private에 접근 가능하니까 외부 클래스 구조를 노출시키기 싫으면 내부 클래스만 호출해서 외부 private 값을 수정하도록 할 수 있음
-> 게터 / 세터랑 비슷한 느낌인듯.
- 코드의 가독성과 유지보수성 향상. 논리적으로 관련 코드가 묶이니까 가독성이 오름
Shadowing
만약 외부의 변수와 동일한 이름 의 변수가 내부에 생기면 호출 시에 더 가까운 변수를 가져와서 바깥쪽 변수가 가려지는 현상
ex1)
public class Box{
public int num = 10; // shadowing 당할 변수
public void method(int num){ // 이름이 똑같음 (!) -> 외부 num을 가림.
num // <-- (매개변수 num이 불러와짐 이 메소드에서 매개변수 num이 인스턴스 변수 num보다 더 가깝기 떄문)
}
}
ex2) 실제로 겪었던 사례 ( 상속 )
class Parent{
protected int a;
protected int b;
Parent(int a, int b){ // parent의 생성자
this.a = a; //섀도잉 방지를 위해서 this. 으로 매겨변수와 인스턴스 변수를 구분해줌
this.b = b;
}
class Child extends Parent{
private int a;
private int b;
Child(int a, int b){
super(a,b); //parent 생성자로 a, b에 지정해주려고 했음
}
// 문제) 이후로 a랑 b 호출할 때마다 값이 없다고 나옴,
}
}
위 코드가 고장났던 이유는 super()로 a와 b에 저장한 값은 child 중 parent의 부분에 저장되는 것인데
-> protected a, protected b에 저장됨
근데 내가 private로 child에 또 a와 b를 선언해서 호출할 때마다 더 가까운 변수 ( private a, private b )를 가져왔던것.
그래서 지정되지 않은 변수를 불러댔으니 값이 나오질 않았다.
-> shadowing 문제임
원래는 super을 하면 그냥 부모의 constructer를 가져와서 쓰는 줄 알고 child에도 a와 b가 있어야 생성자가 제대로 돌아갈 줄 알았으나,
부모 부분이 상속에 의해 자동으로 생성되므로 a, b를 child에 생성할 필요가 없었다.
메소드 안의 클래스 ( Local Class )
말 그대로 메소드에 클래스를 집어넣은 것이다.
선언한 블럭 안에서만 사용할 수 있음 -> 따라서 접근 제어자를 쓸 수 없다 ( abstract, final 제외 )
외부 클래스의 모든 요소에 접근할 수 있음 (Inner Class랑 동일함)
단 이 클래스는 정의된 블록(메소드) 안에서만 호출할 수 있음
익명 클래스 ( Anonymous Class )
이름이 없는 클래스로, 메소드 내부에도 쓸 수 있고,
선언과 동시에 인스턴스를 만들어 낸다. -> new 키워드로 시작함.
한 번만 사용할 수 있음 (정의와 생성을 동시에 하기 때문)
단 특정 클래스를 상속하거나 특정 인터페이스의 구현체여야 함.
-> 혼자서는 사용 불가능. 익명 클래스의 틀이 존재해야함 (상속이던 구현이던)
이때 익명 클래스에서 새로 만든 메소드는 외부에서 호출이 불가능하다
ex)
interface Animal{
void bark();
}
Animal dog = new Animal(){
public void bark(){} // 원래 있던 메소드
public void eat(){} // 새로 만든 메소드
}; // 세미콜론으로 끝나야 한다 (인스턴스 생성이니까)
dog.bark() // 가능
dog.eat() // 불가능
// dog는 Animal 타입인데, Animal에는 eat이 정의되어 있지 않음
간단히 구현만 해서 짧게 쓴다는 느낌으로 부른다.
람다식 ( Lambda Expressions )
인수(변수) -> (실행할 부분)
ex)
x -> System.out.println(x)
x를 받아서 출력함 (x의 타입은 생략해도 무관)
위와 동일한 코드
public void prnt(int x){
System.out,println(x);
}
이걸 간략하게 쓴 거라고 보면 된다.
람다식으로는 메서드 간단하게 쓰기라고 생각하면 됨.
다만 자바에서는 구현의 경우에만 사용 가능함. 틀이 되는 인터페이스가 필요하다.
틀이 되는 인터페이스는 추상 메서드가 하나여야 함 (함수형 인터헤이스)
타입 추론
자바에서는 대충 타입 맞춰 놓으면 알아서 연결해준다.
ex) 오버로딩된 메서드 호출했을때, 매개변수에 맞춰서 호출함
Method Reference
::
키워드로 메소드를 불러내는 것. 메소드를 람다식처럼 전달하게 해줌
종류 | 형태 |
---|---|
static 메소드 참조 | 클래스명 :: 메소드명 |
인스턴스 메소드 참조 | 클래스명.인스턴스명 :: 메소드명 |
클래스 인스턴스 메소드 참조 | 클래스명 :: 메소드명 ( 인스턴스 역할을 함 ) |
생성자 참조 | 클래스명 :: new |
클래스 인스턴스 메소드 <- 이거 머임?
일단 메소드를 정의해주되 인자를 넘겨줘야함
Function<String, Integer> f = String::length;
int len = f.apply("hello");
f에 담긴 메소드에 인자가 들어감
"hello".length() 를 실행하는 것
Enum
특수 데이터 타입임. 클래스 같은건데 딕셔너리 느낌.
ex) Drink Enum이 콜라 사이다 환타를 가진다고 했을때,
public enum Drink {
Coke(2300),
Sprite(2200),
Fanta(2380);
// Drink 타입의 객체들
private int price;
Drink(int price){ // 생성자
this.price = price;
}
public int getPrice(){
return this.price;
}
}
Drink.Coke.getPrice(); // 2300.
'자바' 카테고리의 다른 글
[JAVA] 컴퓨터 프로그래밍 - 인터페이스 (0) | 2025.04.28 |
---|---|
[JAVA] 컴퓨터 프로그래밍 - 변수 기초 (0) | 2025.04.27 |
[JAVA] 컴퓨터 프로그래밍 - Class 기초 (0) | 2025.04.27 |
[JAVA] 컴퓨터 프로그래밍 - 개념 정리 (0) | 2025.04.27 |
[JAVA] 컴퓨터 프로그래밍 - 인터페이스 vs Abstract 클래스. (0) | 2025.04.24 |