익명 구현 객체(무명 클래스) anonymous class => 인터페이스로 구현
-명시적으로 구현클래스를 작성하지 않고, 바로 구현 객체를 얻는 방법이다.
이름이 없는 클래스라서 익명이라고 한다.
-인터페이스의 추상 메서드를 익명구현객체라고 해도 다시 재정의를 하여야 한다.
-추가적으로, 필드와 메서들를 선언 가능하나 익명 구현객체 안에서만 사용 가능하다.
-외부에서 접근 불가
-인터페이스 참조변수로 접근 역시 불가능하다.,(원래 타입이 없다. 객체생성 불가)
package kr.co.kihd.anonymous;
//인터페이스에 추상메서드가 1개만 오로지 존재하면 함수적 인터페이스.(람다식)
public interface Soundable {
public void sound();
}
package kr.co.kihd.anonymous;
public class SoundTest {
public static void main(String[] args) {
//익명구현객체(이름 없음) -- UI이벤트 처리나 (스레드 객체) 간편하게 생성시 사용.
//잠깐 사용할 목적
Soundable soundable = new Soundable() { // (원래)타입이 인터페이스
//내부적으로 클래스이니까 멤버들을 선언가능
//하지만, 외부에서 접근 불가
int a = 10;
public void method() {
System.out.println("익명구현객체 메서드");
}
@Override
public void sound() {
String str = "냄비";
System.out.println("첫번째 익명구현객체: " + str);
System.out.println(str + "에 물을 담습니다.");
System.out.println(str + "에 숟가락으로 탕탕소리를 냅니다.");
this.method();
System.out.println(this.a);
}
};
Soundable soundable1 = new Soundable() { // (원래)타입이 인터페이스
//내부적으로 클래스이니까 멤버들을 선언가능
//하지만, 외부에서 접근 불가
int a = 20;
public void method() {
System.out.println("밥그릇 익명구현객체 메서드");
}
@Override
public void sound() {
String str = "밥그릇";
System.out.println("두번째 익명구현객체: " + str);
System.out.println(str + "에 물을 담습니다.");
System.out.println(str + "에 숟가락으로 탕탕소리를 냅니다.");
this.method();
System.out.println(this.a);
}
};
soundable.sound();
soundable1.sound();
}
}
익명 자손 객체(무형 클래스) => 클래스로 구현
명시적으로 자손클래스를 작성하지 않고, 바로 자손 객체를 얻는 방법이다.
-이름이 없는 클래스라서 익명이라고 한다.
-원 타입이 조상클래스 타입이라서, 통상 익명 자손 객체는 조상의 메서드를 오버라이딩하여 잠시 사용할 용도로 사용한다.
(UI이벤트 처리나 쓰레드 객체를 간편히 생성)
-추가적으로, 필드와 메서드를 선언가능하나 익명 자손 객체 안에서만 사용 가능하다.
외부에서 접근 불가
-조상타입 참조변수로 접근 역시 불가능하다.(원래타입이 없다.)
package kr.co.kihd.anonymous;
public class Parent {
public Parent(){
System.out.println("조상 생성자");
}
public void method() {
System.out.println("Parent 메서드");
}
}
package kr.co.kihd.anonymous;
public class ChildTest {
public static void main(String[] args) {
//익명자손객체(이름없음) -- UI이벤트 처리나 스레드 객체 간편하게 생성시 사용.
Parent parent = new Parent() { //원래타입이 클래스임.
int b = 10;
public void method1(){
System.out.println("익명자손객체 메서드");
}
@Override
public void method() {
int a = 10;
System.out.println("a : " + a);
System.out.println(this.b);
this.method1();
}
};
parent.method();
// parent.b; 부모타입 멤버가 아님
// parent.method1(); 부모타입 멤버가 아님
}
}
중첩(내부) 클래스 - inner(nested) class
중첩 클래스의 개념
-클래스 안에 멤버로써 선언되어진 클래스를 칭한다.
-해당 클래스 내에서만 사용되어질 클래스를 중첩클래스로 선언한다.,(외부에서 사용 안함)
-GUI애플리케이션(AWT, SWING, 안드로이드 프로그램)의 이벤트 처리에서 주로 사용된다.
또한 웹애플리케이션에서 많이 사용된다.
중첩클래스의 장점
-중첩클래스에서 외부클래스의 멤버에 쉽게 접근이 가능하다.
-코드의 복잡성을 줄일 수 있다.(캡슐화)
중첩클래스의 종류와 특징
중첩 클래스의 종류는 변수의 선언위치에 따른 종류와 동일하다
유효범위 및 성질도 변수와 유사하다.
중첩클래스의 바이트 코드 파일
A $ B .class 외부클래스 $ 멤버클래스,
A $1 B .class 외부클래스 $1 로컬클래스.
(멤버클래스와 로컬 클래스는 순번으로 구분을 짓는다)
인스턴스 멤버클래스
외부 클래스가 생성되어야, 내부 클래스를 사용할 수가 있다.
*** static은 인스턴스 생성 없이도 접근이 가능해야 하므로, 인스터스 멤버클래스에는
멤버변수나 메서드로 선언이 불가함에 주목하자,
언제 인스턴스가 생성될지는 아무도 모른다. 아여, 인스턴스 멤버 클래스에는
static멤버가 올 수가 없다.
항상 A의 인스턴스가 생성 되어야함.
A.B b = a. new B()
정적 멤버 클래스
외부 클래스 A가 생성되지 않아도 접근가능하다.
***인스턴스 멤버변수는 new생성자를 통해 인스턴스가 만들어져야만 접근가능하다.
static 키워드로 선언된 클래스, 모든 종류의 필드. 메서드 선언가능
로컬 클래스 - 메서드 내 작성 후 사용
기능적인 것들을 먼저 정의 -> 객체생성
Thread클래스의 run 메서드 사용
지역클래스는 메서드 내에서 선언과 사용
package kr.or.kihd.nested;
public class NestedTest {
//인스턴스 멤버 클레스
class A {
int iv = 100;
// static int cv = 200; //static 멤버 변수는 사용불가.
public A() {
System.out.println("A생성자 호출");
}
public void method() {
System.out.println("A클레스의 method()호출");
}
// public static void method2() {
//
// }
}
//정적 멤버 클레스
static class B {
int iv = 100;
static int cv = 500; // NestedTest.B.cv 형태로 접근하면 된다.
public B() {
System.out.println("B 생성자 호출");
}
public void method1() {//인스턴스 메서드
System.out.println("B클레스의 method1()호출");
}
static void method2() {//정적 메서드
System.out.println("B클레스의 method1()호출2");
}
}
public void method1() {
//로컬클레스 : 메서드내에 선언과 사용(잠깐 사용할 용도) 이벤트처리 스레드 처리
class C {
int iv = 200;
// static cv = 700; //static 멤버 선언 불가
public C() {
System.out.println("로컬클레스C 생성자 호출");
}
public void Imethod() {
System.out.println("로컬클레스C 메서드 호출");
}
}
//로컬클래스는 해당 메서드 내에서만 사용이 가능하다는 것에 주목하자.
C c = new C();
System.out.println(c.iv);
c.Imethod();
}
public static void main(String[] args) {
NestedTest nestedTest = new NestedTest();
//인스턴스 멤버 클레스는 외부클레스의 인스턴스가 반드시 있어야 생성가능함.
NestedTest.A a = nestedTest.new A();
System.out.println(a.iv);
a.method();
System.out.println("-------------------------------");
//정적 클레스 접근 : 외부클레스의 객체생성없이 인스턴스가 없이도 접근 가능함.
System.out.println(NestedTest.B.cv);
NestedTest.B.method2();
System.out.println("-------------------------------");
//로컬 클래스가 포함된 메서드 호출
nestedTest.method1();
System.out.println("-------------------------------");
}
}
인스턴스 클레스 , 정적 멤버 클래스 예제
package kr.or.kihd.nested;
public class A {
public A() {
System.out.println("-----------------------------------------");
System.out.println("외부클래스 A의 생성자 호출");
B b = new B(); //인스턴스 멤버클레스는 외부클레스 A의 멤버라서 객체생성 가능
System.out.println(b.a);
b.method1();
C c = new C();
System.out.println(c.a);
System.out.println(C.c);
c.method1();
c.method2();
}
//인스턴스 멤버클레스 B
class B {
int a = 10;
// static int cv = 20; //클레스가 언제생성될지 몰라서
public B() {
System.out.println("중첩클레스 B의 생성자 호출");
}
public void method1() {
System.out.println("B클레스 멤버메서드 호출");
}
// static void method2() {
//
// }
}
//정적 멤버 클래스 C
static class C{
int a = 10;
static int c = 20;
public C() {
System.out.println("정적멤버클레스 C의 생성자 호출");
}
public void method1() {
System.out.println("정적멤버클레스 C의 method1() 호출");
}
static public void method2() { //정적메서드
System.out.println("정적멤버클레스 C의 정적메서드 method2() 호출");
}
}
}
package kr.or.kihd.nested;
public class ATest {
public static void main(String[] args) {
A a = new A();
//인스턴스 클레스인 B는 반드시 외부 클레스 A의 인스턴스가 있어야
//생성이 가능하다.
A.B b = a.new B();
b.method1();
System.out.println("-----------------------------------------");
System.out.println(A.C.c);
A.C.method2();
//정적 멤버 클래스인 C는 외부클레스 A의 인스턴스가 있던 없던
//상관없이 접근 가능하다.
A.C c = new A.C();
System.out.println(c.a);
c.method1();
System.out.println("------------------------------------------");
}
}
중첩클래스 접근제한 예제
결론 : 중첩클래스에서의 인스턴스 필드나 메서드는 언제든지 생성가능하지만,
정적필드나 정적메서드는 정적클래스에서만 가능하다.
package kr.or.kihd.nested2;
public class Aaccess {
//인스턴스 멤버클래스
class B {
public B() {
System.out.println("인스턴스 멤버 클래스B 생성자");
}
}
//정적 멤버클래스
static class C {
public C() {
System.out.println("정적멤버클래스C 생성자");
}
}
//인스턴스 멤버(필드)
B b = new B();
C c = new C();
//인스턴스 멤버 메서드
//method1()을 사용할때 쯤은, 이미 외부클래스 Aaccess의
//인스턴스가 만들어진 상태일것이다.
public void method1() {
System.out.println("method()");
//지역변수
B b = new B();
C c = new C();
}
//정적 멤버
// static B b1 = new B(); //(X) B클래스는 인스턴스 멤버 클래스이기 떄문에,
//static이 붙으면 안됨.
static C c1 = new C();
Aaccess.B c2 = new B();
// static Aaccess.B c3 = new B(); //(X) B클래스는 인스턴스 멤버 클래스이기 떄문에,
//static이 붙으면 안됨.
//static은 static만 접근이 가능하다.
public static void method2() {
System.out.println("method2");
// B b = new B(); //(X) B클래스는 인스턴스 멤버 클래스이기 떄문에,
//static이 붙으면 안됨.
C c = new C(); //정적메서드 이기떄문에 정적 로컬 변수로 선언을 해도 무방함.
}
//결론 : 중첩클래스에서의 인스턴스 필드나 메서드는 언제든지 생성가능하지만,
//정적필드나 정적메서드는 정적클래스에서만 가능하다.
}
package kr.or.kihd.nested2;
public class ATest {
public static void main(String[] args) {
Aaccess a = new Aaccess();
Aaccess.B b = a.new B();
a.method1();
}
}
중첩클래스의 맴버들에게 접근 예제
package kr.or.kihd.inneroutter;
public class Outside {
//필드선언
String str = "Outside-field";
//멤버메서드
public void method() {
System.out.println("Outside-method() 호출");
}
class Inner {
//필드선언
String str = "Inner-field";
//멤버메서드
public void method() {
System.out.println("Inner-method() 호출");
}
//멤버 메서드
public void showInfo() {
//여기에서 이this는 Inner클래스의 this이다
System.out.println(this.str);
this.method();
//외부클래스의 접근하는 방법
//외부클래스명.this.외부클래스멤버이름
System.out.println(Outside.this.str);
Outside.this.method();
}
}
public void show() {
System.out.println("--------------------------------");
//외부클래스 영역에서의 this는 당연히 Outside클래스를 말한다.
System.out.println(this.str);
this.method();
//외부클래스에서 내부클래스로 접근을 할려면 보이지 않는다.
//인스턴스를 하나 생성해서 중첩클래스의 맴버들에게 접근하면 된다.
Inner inner = new Inner();
inner.showInfo();
}
}
package kr.or.kihd.inneroutter;
public class OutsideTest {
public static void main(String[] args) {
Outside outside = new Outside();
Outside.Inner inner = outside.new Inner();
inner.showInfo();
outside.show();
}
}