-익명 객체 : 이름이 없는 객체
-단독 생성 불가 : 클래스 상속하거나 인터페이스 구현해야만 생성가능
-사용 위치 : 필드나 로컬변수의 초기값, 매개변수의 매개값으로 주로 대입
상속관계 : 부모-자식 / 상위-하위
연결관계 : 인터페이스 - 구현클래스
부모 타입으로 필드나 변수 선언 후, 자식 객체를 초기값으로 대입할 경우를 생각해보자. 부모를 상속해 자식클래스를 선언하고, new 연산자로 자식 객체를 생성한 후, 필드나 로컬 변수에 대입하는것
// 자식 클래스 선언
class Child extends Parent { }
class A {
// 필드에 자식 객체 대입
Parent field = new Child();
void method() {
// 로컬 변수에 자식 객체 대입
Parent local = new Child();
}
}
but. 자식 클래스가 재사용되지 않고, 오로지 해당 필드와 변수의 초기값으로만 사용할 경우라면 익명 자식 객체를 생성해 초기값으로 대입하는 것이 좋다
부모클래스 필드변수 = new 부모클래스 (매개값, ...) {
// 필드
// 메소드
};
주의할 점은 하나의 실행문이기에 저렇게 세미콜론(;)을 붙여야한다는 것이다.
// 1. 부모 클래스를 만들어준다
class Parent {
void pM() { }
void pM1() { }
}
class A {
// 부모 객체 생성과 동시에 자식 객체 생성
Parent p = new Parent() {
String cName;
boolean gender;
void cM1() { }
void cM2() { }
// 익명 자식객체는 외부에서 호출할 수 없기때문에
// 오버라이딩한 메소드에서 사용
@Override
void pM() {
cName="강아지";
cM1();
}
@Override
void pM1() {
cName="고양이";
cM2();
}
};
// 클래스 내의 다른 메소드
void aMethod() {
// 익명 객체의 것들이기에
// 그 메소드를 넘어가면 호출 불가
// p.cName = "홍길동";
// p.gender = true;
// p.cM1();
// 이건 부모 클래스의 것을 오버라이딩 한거니 사용 가능
p.pM();
p.pM1();
}
}
부모가 될 Person을 받아와 객체 생성을 하고 자식인 익명 객체 구현
Person field = new Person() {
// 익명클래스의 메소드는 오버라이딩한 메소드에서 사용하기 위해 작성
void work() {
System.out.println("출근합니다");
}
@Override
void wake() {
System.out.println("6시에 일어납니다");
// 익명 자식 클래스 메소드 호출
work();
}
};
-클래스 내에 메소드를 만들고, 그 안에 상위 클래스를 new로 객체 생성한 뒤 실행내용을 그 안에 작성하고 사용한다.
-내용을 실행하기 위해서는 메소드 안에 작성한 걸 호출해놔야 한다.
void method1() {
//로컬 변수값으로 대입
Person localVar = new Person() {
void walk() {
System.out.println("산책합니다");
}
@Override
void wake() {
System.out.println("7시에 일어납니다");
// 익명 자식 클래스 메소드 호출
walk();
}
};
//로컬 변수 사용
localVar.wake();
}
매개값을 받는 메소드를 만든 후, 부모 클래스의 것으로 연결한다.
// 매개값으로 Person을 받아 변수 person에 저장
// 들어오는 값을 person.wake()에 받는다
void method2(Person person) {
person.wake();
}
public class AnonymousEx {
public static void main(String[] args) {
Anonymous anony = new Anonymous();
// wake()는 자식 객체에 있지만
// 오버라이딩 한 것이기에 사용 가능한 것
// 익명 객체 필드 사용
anony.field.wake();
// 익명 객체 로컬 변수 사용
anony.method1();
// 익명 객체 매개값 사용
// 매개값으로 new Person()을 올려 익명 객체 구현함
anony.method2(new Person() {
void study() {
System.out.println("공부합니다");
}
@Override
void wake() {
System.out.println("8시에 일어납니다");
study();
}
}
// 잘보자. } 이거 아니고 )이거다
);
}
}
실행모습
public class AnonyEx {
public static void main(String[] args) {
A field1 = new A() {
int childField = 10;
void childM() {
System.out.println("익명 구현 메소드 구역");
}
@Override
void aM() {
System.out.println("익명 자식 구현 객체 값 : " + childField);
// 익명 자식 구현 메소드 값 호출
childM();
}
};
field1.aM();
}
}
class A {
void aM() { }
}
public class AnonyEx2 {
public static void main(String[] args) {
A1 local = new A1() {
double d = 3.14;
void dA() {
System.out.println("로컬 익명 객체 메소드");
}
@Override
void a1M() {
System.out.println("익명 로컬 변수 d의 값 : " + d);
// 익명 메소드 호출
dA();
}
};
local.a1M();
}
}
class A1 {
void a1M() { }
}
public class AnonyEx3 {
public static void main(String[] args) {
BussinesLogic bl = new BussinesLogic(new A2() {
// A의 필드
int num = 10;
byte num2 = 20;
char ch = 'C';
// A의 메소드
void aChild() {
System.out.println("A의 메소드 부분");
}
@Override
void a2M() {
System.out.println("num + 10 : " + (num + 10));
System.out.println("byte + 20 : " + (num2 + 20));
System.out.println("char ch : " + ch);
aChild();
}
});
}
}
class BussinesLogic {
// 생성자
// 오버로딩 함
public BussinesLogic() { }
public BussinesLogic(A2 a) { }
}
class A2 {
void a2M() { }
}
public interface RemoteControl {
public void turnOn();
public void turnOff();
}
public class Anonymous {
// 필드 초기값으로 대입
RemoteControl field = new RemoteControl() {
@Override
public void turnOn() {
System.out.println("Tv를 켜겠습니다");
}
@Override
public void turnOff() {
System.out.println("Tv를 끄겠습니다");
}
};
void method1() {
// 로컬 변수값으로 대입
RemoteControl localVar = new RemoteControl() {
@Override
public void turnOn() {
System.out.println("Audio를 켭니다");
}
@Override
public void turnOff() {
System.out.println("Audio를 끕니다");
}
};
// 로컬변수 호출
localVar.turnOn();
}
void method2(RemoteControl rc) {
rc.turnOn();
}
}
public class AnonymousEx {
public static void main(String[] args) {
Anonymous anony = new Anonymous();
// 익명 객체 필드
anony.field.turnOn();
// 익명 객체 로컬 변수 사용
anony.method1();
// 익명 객체 매개값 사용
anony.method2(new RemoteControl() {
@Override
public void turnOn() {
System.out.println("스마트 티비를 켭니다");
}
@Override
public void turnOff() {
System.out.println("스마트 티비를 끄겠습니다");
}
});
// 익명 객체 필드
anony.field.turnOff();
}
}
실행 모습
interface In {
void play();
void stop();
}
로컬 변수에 익명 구현 객체 만들기
매개변수를 인터페이스로 주고 받을 수 있을까?
class BL {
// 필드로 익명 구현 객체 만들기
In infield = new In() {
@Override
public void stop() {
System.out.println("필드 멈춤");
}
@Override
public void play() {
System.out.println("필드 실행");
}
};
// 로컬 변수에 익명 구현 객체 만들기
// 만약 변수 선언을 메소드 밖에 한다면
In local;
void blM1() {
// 익명객체 내부에서 매개변수나 로컬 변수를 사용할 경우
// 이 변수들은 final을 생략해도 final의 특성을 가진다
// 1. 변수 a 를 10이라 초기화했다
int a = 10;
local = new In() {
@Override
public void stop() {
System.out.println("로컬 멈춤");
// 2. 익명객체 내부에서 다른 값에 a를 연산하여 넣는 것은 가능하나
int b = a + 10;
// a 그 자체의 값을 변경하는 것은 불가능
// a += 10;
}
@Override
public void play() {
System.out.println("로컬 실행");
}
};
}
// 다른 메소드에서 로컬변수 호출도 가능하다
void yes() {
local.stop();
local.play();
}
// 매개변수를 인터페이스로 주고받는 메소드
void blM2(In i) {
i.stop();
i.play();
}
// 매개변수값 으로 익명 구현 객체 만들기
void yes2() {
blM2(new In() {
@Override
public void play() {
System.out.println("매개변수값 실행");
}
@Override
public void stop() {
System.out.println("매개변수값 정지");
}
});
}
}
public class AnonyEx {
public static void main(String[] args) {
BL bl = new BL();
bl.infield.play();
bl.infield.stop();
System.out.println("-----------------");
bl.blM1();
bl.yes();
System.out.println("------------------");
bl.blM2(new In() {
@Override
public void stop() {
System.out.println("메인에서 매개변수 넘겨준 중지");
}
@Override
public void play() {
System.out.println("메인에서 매개변수 넘겨준 실행");
}
});
System.out.println("------------------");
bl.yes2();
}
}
실행 모습
메소드 안에 익명구현 객체를 포함하고 있다면, 그 매소드의 매개변수와 지역변수는 자동 final 로 간주된다.
단, 메소드 밖에서 설정한 변수인 field 값은 변경 가능
예제)
public interface Calcu {
public int sum();
}
// 메소드 안에 익명구현 객체를 포함하고 있다면
// 그 매소드의 매개변수와 지역변수는 자동 final 로 간주된다
class Anonymous {
private int field;
public void method(final int arg1, int arg2) {
final int var1 = 0;
int var2 = 0;
// 메소드 밖에서 설정한 변수인 field 값은 변경 가능
field = 10;
// arg1 = 20;
// arg2 = 20;
// var1 = 30;
// var2 = 30;
// 익명 구현 객체에서 변수를 참조하고 있기 때문에
// 메소드 내에 호출된 변수는 그 상태로 final 고정 되어 값 변경이 불가하다
Calcu calc = new Calcu() {
@Override
public int sum() {
// field는 전역변수라 값 변경 가능
field = 30;
int result = field + arg1 + arg2 + var1 + var2;
return result;
}
};
System.out.println(calc.sum());
}
}
메소드 내에서 생성된 익명 객체는 메소드 실행이 끝나도 힙 메모리에 존재하기에 계속 사용 가능하다. 매개변수나 로컬변수는 메소드 실행이 끝나면 스택 메모리에서 사라지기 때문에 익명 객체에서 사용할 수 없으므로 문제가 발생한다
-> 익명객체 내부에서 메소드의 매개 변수나 로컬 변수를 사용할 경우, 이 변수들은 final 특성을 가져야한다. 컴파일시 final 키워드가 있다면 메소드 내부에 지역 변수로 복사되지만, final 키워드가 없다면 익명 클래스의 필드로 복사된다.