클래스 내에 정의된 클래스를 가리켜 네스티드 클래스라고 한다. 이를 감싸는 클래스를 가리켜 이너 클래스라고 한다.
네스티드 클래스의 구분
기본적으로 클래스 내에 정의된 모든 클래스를 가리켜 네스티드 클래스라고 한다. 네스티드 클래스는 static 선언 여부를 기준으로 나뉜다.
Static 네스티드 클래스
Non static 네스티드 클래스 (=inner 클래스)
Class Ouster{
Static class staticNestedClass {
} -> static 네스티드 클래스
Class InnerClass
Non static 네스티드 클래스
}
Static 네스티드 클래스
Static 네스티드 클래스는 static 선언이 갖는 특성이 반영된 클래스. 외부 클래스의 인스턴스와 상관없이 static 네스티드 클래스의 인스턴스 생성이 가능하다.
class Outer{
private static int num = 0;
static class Nested1{
void add(int n) {
num += n;
}
}
static class Nested2{
int get(){
return num;
}
}
}
public class Main {
public static void main(String[] args) {
Outer.Nested1 nested1 = new Outer.Nested1();
nested1.add(5);
Outer.Nested2 nested2 = new Outer.Nested2();
System.out.println(nested2.get());
}
}
Static Nested Class
static class Nested1{
void add(int n) {
num += n;
}
}
static class Nested2{
int get(){
return num;
}
}
}
Nested1 ,2 클래스 내에서 Outer static 맴버 num 접근. Private 선언되어 있어도 접근이 가능하다. Outer static 맴버 num은 Nested1, 2의 모든 인스턴스 공유
Static 네스티드 클래스의 인스턴스 생성은 외부 클래스의 인스턴스 생성과 무관. (외부 클래스의 인스턴스 생성 X static 네스티드 클래스의 인스턴스 생성이 가능하다)
Inner 클래스의 구분
Static 선언이 붙지 않은 클래스를 가리켜 이너 클래스
Member Class
인스턴스 변수, 인스턴스 메소드와 동일한 위치에 정의
Local Class
중괄호 내에 특히 메소드 내에 정의
반면 익명 클래스의 이름이 존재 하지 않는 이름 그대로 익명 클래스
class Outer1{
private int num = 0;
class Member{
void add(int n){
num += n;
}
int get(){
return num;
}
}
}
public class Main1 {
public static void main(String[] args) {
Outer1 out = new Outer1();
Outer1 out2 = new Outer1();
Outer1.Member m1 = out.new Member();
Outer1.Member m2 = out.new Member();
Outer1.Member m3 = out2.new Member();
Outer1.Member m4 = out2.new Member();
m1.add(5);
System.out.println(m2.get());
m3.add(7);
System.out.println(m4.get());
}
}
class Outer1{
private int num = 0;
class Member{
void add(int n){
num += n;
}
int get(){
return num;
}
}
}
MemberClass 내에서 Outer 클래스의 인스턴스 변수에 접근이 가능. 인스턴스 변수가 private으로 선언 되어 있어도 가능하다.
“맴버 클래스의 인스턴스는 외부 클래스의 인스턴스에 종속적”
Outer1 out = new Outer1();
Outer 인스턴스 없이 Member 인스턴스 생성 불가능
Outer1.Member m1 = out.new Member();
두 인스턴스 out이 참조하는 인스턴스의 맴버에 접근 할 수 있다. 두 인스턴스는 out이 참조하는 인스턴스 맴버를 공유
맴버 클래스를 언제 사용?
“클래스 정의를 감추어야 할 때 유용하게 사용”
interface Printable{
void print();
}
class Papers{
private String con;
public Papers(String s){
con = s;
}
public Printable getPrinter(){
return new Printer();
}
private class Printer implements Printable{
public void print(){
System.out.println(con);
}
}
}
public class Main2 {
public static void main(String[] args) {
Papers p = new Papers("서류 내용 행복합니다.");
Printable prn = p.getPrinter();
prn.print();
}
}
맴버 클래스 인스턴스 생성 그리고 반환
public Printable getPrinter(){
return new Printer();
}
맴버 클래스의 정의
private class Printer implements Printable{
public void print(){
System.out.println(con);
}
}
}
printable instance 생성
interface Printable{
void print();
}
printer 클래스를 다음과 같이 papers 클래스 내에 정의
class Papers{
private String con;
public Papers(String s){
con = s;
}
public Printable getPrinter(){
return new Printer();
}
private class Printer implements Printable{
public void print(){
System.out.println(con);
}
}
}
그런데 private Printer 이다. 멤버 클래스가 private으로 선언되면 클래스 정의를 감싸는 클래스 내에서만 인스턴스 생성 가능.
public static void main(String[] args) {
Papers p = new Papers("서류 내용 행복합니다.");
Printable prn = p.getPrinter();
prn.print();
}
이런 방법으로 참조가 가능하다.
Papers 클래스 외부에선 getPrinter 메소드가 어떤 인스턴스 참조 값 반환하는지를 알지 못한다. 다만 반환되는 참조 값의 인스턴스가 Printable을 구현 하고 있어서 Printable의 참조 변수로 참조 할 수 있다는 사실만 알 뿐이다. 이런 상황을 클래스의 정의가 감추어진 상황 이라고 한다.
getPrinter 메소드가 반환하는 인스턴스가 다른 클래스의 인스턴스로 변경되어도 Papers 클래스 외부 코드는 수정 할 필요 없다.
로컬 클래스
멤버 클래스와 로컬 클래스는 상당히 유사하다. -> 단 클래스 정의가 if문 이나 while 또는 메소드 몸체와 같은 블록 안에 정의된다는 점에서 구분
interface Printable1{
void print();
}
class Papers1{
private String con;
public Papers1(String s){
con =s;
}
public Printable1 getPrinter(){
class Printer1 implements Printable{
public void print(){
System.out.println(con);
}
}
return (Printable1) new Printer1();
}
}
public class Main3 {
public static void main(String[] args) {
Papers1 p = new Papers1("서류");
Printable1 able = p.getPrinter();
able.print();
}
}
public Printable1 getPrinter(){
class Printer1 implements Printable{
public void print(){
System.out.println(con);
}
}
return (Printable1) new Printer1();
}
private class Printer implements Printable{
public void print(){
System.out.println(con);
}
}
}
둘 차이는 메소드 안에 클래스 정의 위치
매소드 내에 클래스를 정의하면 해당 메소드 내에서만 인스턴스 생성이 가능하다. 즉, 맴버 클래스보다 더 깊게 숨길 수 있다.
익명 클래스
클래스 Printer 정의와 Printer 인스턴스의 생성이 분리.
public Printable1 getPrinter(){
class Printer1 implements Printable{ //printer 정의
public void print(){
System.out.println(con);
}
}
return (Printable1) new Printer1(); //printer 인스턴스 생성
}
}
익명 클래스의 형태로 정의하면 클래스의 정의와 인스턴스 생성을 하나로 묶을 수 있다.
public Printable2 getPrinter(){
return new Printable2() {
@Override
public void print() {
System.out.println(con);
}
};
}
}
클래스 정의와 인스턴스의 생성을 하나로 묶을 수 있다.
해당 인터페이스를 구현하는 클래스의 정의를 덧붙이면 인스턴스 생성 가능
@Override
public void print() {
System.out.println(con);
}
};
New Printable()에 이어 등장하는 이름 없는 클래스의 정의를 가리켜 익명 클래스라고 한다.
interface Printable2{
void print();
}
class Papers2{
private String con;
public Papers2(String s){
con = s;
}
public Printable2 getPrinter(){
return new Printable2() {
@Override
public void print() {
System.out.println(con);
}
};
}
}
public class Main4 {
public static void main(String[] args) {
Papers2 p = new Papers2("HEllo");
Printable2 print = p.getPrinter();
print.print();
}
}
컬랙션 프레임워크 관련 예제
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class StrComp implements Comparator{
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
}
public class Main5 {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("ROBOT");
list.add("TOY");
list.add("CANDY");
StrComp str = new StrComp(); //정렬 기준
Collections.sort(list, str); // 정렬 기준 변경해서 정렬
System.out.println(list);
}
}
이를 익명 클래스 기반으로 수정하면 람다가 등장하기 이전에 사용되던 코드
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main6 {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("TOY");
list.add("ROBOT");
list.add("BOX");
Comparator<String> cmp = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
};
Collections.sort(list, cmp);
System.out.println(list);
}
}
람다
람다의 이해
interface Printable5{
void print(String s);
}
class Printer2 implements Printable5{
@Override
public void print(String s) {
System.out.println(s);
}
}
public class Main7 {
public static void main(String[] args) {
Printable5 able = new Printer2();
able.print("LAMBDA??");
}
}
인터페이스 Printable과 이를 구현하는 클래스 Printer의 정의 핵심
interface Printable6{
void print(String s);
}
public class Main8 {
public static void main(String[] args) {
Printable6 print = new Printable6() {
@Override
public void print(String s) {
System.out.println(s);
}
};
print.print("LAMBDA");
Printable6 print2 = (s) -> {
System.out.println(s);
};
print2.print("?");
}
}
이를 람다식으로 변환
interface Printable8 {
void Print(String s);
}
public class Main9 {
public static void ShowString(Printable8 p, String s) {
p.Print(s);
}
public static void main(String[] args) {
ShowString((s) -> {
System.out.println(s);
}, "?");
}
람다와 익명 클래스는 분명히 다르다. 그러나 둘 다 인스턴스의 생성으로 이어지고, 람다식이 익명 클래스의 정의를 일부 대체하기 때문에 익명 클래스의 정의를 기반으로 람다식을 이해하는 것도 좋은 방법
};
S가 매개 변수라는 것을 모르기 때문에
4.
Printable6 print = (S) ->{ System.out.println(s); };
람다식 인자 전달
매개변수 초기화 가능, 참조변수 초기화 가능, 람다식을 메소드 인자로 전달 가능
interface Printable8 {
void Print(String s);
}
public class Main9 {
public static void ShowString(Printable8 p, String s) {
p.Print(s);
}
public static void main(String[] args) {
ShowString((s) -> {
System.out.println(s);
}, "?");
}
}
첫 번째 매개변수를 초기화
ShowString((s) -> {
System.out.println(s);
}, "?");
}