Java

[Java-25] 클래스 & 내부클래스 & 익명클래스

lee-maru 2020. 12. 9. 12:02

Java Class

🐵 Java 클래스


🙈 클래스?

어떤 객체지향 언어에서든지, 클래스라는 개념을 한번쯤은 들어봤을 것이다. 먼저 간단한 코드를 분석해보자,

  • 자바에서 클래스를 선언
public class App {
}

정말 쉽다, 이런식으로 public + class + name 다음과 같이 만들면 클래스 하나가 생성된 거다.
이제 클래스를 직접 만들어보면 애완동물 클래스인 Pet을 만들고, 여기에 변수도 넣어보자

 public class Pet {

    String kind; // 펫의 종류
    String name; // 펫 이름
    Integer age; // 펫 나이

}

이렇게 만들어 놓은 클래스는 우리가 마음대로 찍어낼 수 있다, 내가 강아지를 키우고 있다면 클래스를 변수로 선언하여, 클래스에다가 마음대로 집어넣을 수 있다. 마치 C의 구조체와 같이 말이다.

    public static void main(String[] args) {
        Pet pet = new Pet();
        pet.age = 10;
        pet.kind = "dog";
        pet.name = "복실이";
        System.out.println(pet.toString());
    }
  • toString : 객체가 가진 정보를 String 으로 만들어 주는 함수 @Overriding 해야함
    Pet 클래스를 선언함으로 써, 유저가 원하는 다양한 객체를 클래스를 통해서 선언이 가능해진다.
    우리는 이 과정을 인스턴스 화 라고 한다.

    결과 콘솔

    > Pet{kind='dog', name='복실이', age=-10}

🙉 이런 클래스를 가진 OOP 의 특징이 있다.

무결성, 은닉성, 캡슐화

  • 이들의 특징과 장점은 나중에 알아보도록 하고, 이런게 있구나하고 넘어가도록 하자, Getter를 왜 사용하는지
  • Setter를 왜 사용하는지, 그리고 class안에 method를 정의하면서 어떻게 활용이 가능한지 알 수 있다.

🙉 객체 생성부터 변수 생성까지


IMG_32A7E147AB41-1

  • Class 는 이미 생성된 상황에서, 원하는 클래스에 Data 를 타입에 맞게 넣어놓고 객체를 생성할 수 있다. 책에서는 이 과정을 인스턴스 화 한다고 얘기한다.

  • 이렇게 저장된 데이터는 JVM 메모리 영역의 Heap 에 올라가게 된다.


🐵 내부 클래스 (Inner Class)

🙈 내부 클래스?

내부 클래스는 말 그대로 클래스 안에 클래스가 있다는 소리다. 한가지 예를 들어보자
Java 로 구현한 Stack 코드 using Node

public class ListNodeStack { // 일반적인 클래스, 외부 클래스
    Node topNode; // 내부클래스 Node 객체를 생성

    @Getter
    class Node{ // 내부 클래스
        int data;
        Node next;

        public Node (int data){
            this.data = data;
        }
    }
}

ListNodeStack 이라는 클래스 안에서, Stack 을 구현하기 위한 Node를 내부 클래스로 선언을 했다. 즉, 내부클래스는 일반적으로, 상위 클래스와 밀접환 연관이 있을 때 사용한다.

캡슐화

public class Computer{

  Cpu cpu;
  Memmory memory;

  class Cpu{
    String company;
    Integer price;
         String model;
    Double hz;
  }
  class Memory{
    String company;
    Integer memorySize;
    String model;
  }
  ...
}
  • 이렇게 서로 연관되어있는 클래스를 이너클래스로 구현하여, 캡슐화의 근간이 된다.

🐵 익명 클래스

일반적인 클래스 사용법
public class Pet{
    String name = "돼지";
    public String getName(){
            return name;
    }
}
public static main(String args[]){
    Pet pet = new Pet(); // 인스턴스 선언 
}

🙈 익명 클래스

  • 이번에는 익명클래스에 대해서 알아보는 시간을 가져보도록 하자
  • 익멸 클래스는 이름이 지정되어 있지 않은 클래스를 의미한다.
    정확히 익명클래스를 선언하는 방법에는 2가지가 있다.

🙉 익명클래스를 사용하는 방법

1. Interface를 implemets 를 받아 사용한다.

2. Class를 상속 받아서 사용한다.

1. 인터페이스 implements 사용법

public interface Monster{
    String getName();
} 
public static void main(String args[]){
    Monster monster = new Monster(){
        String name;
        public String getName(){
            return name;
        }
    };
    System.out.println(monster.getName());
}

이 형식 어디서 많이 보지 않았는가? 만약 쓰레드를 공부했다면 Runnable 과 같은 형태라는 걸 눈치채챌 수 있다. 나중에 설명하겠지만 Runnable, Thread 선언 하는 이 두가지 방법도 비슷한 방식이라고 생각 하면된다. Thread는 조만간 다뤄보도록 하자 : D

2.익명 클래스 상속 사용법

public class Pet{
    String name = "돼지";
    public String getName(){
            return name;
    }
}
public static void main(String[] args){
    Pet pet = new Pet(){
            String name = "익명 돼지";
            @Override
            public String getName(){
                return name;
            }
    };
    System.out.println(pet.getName()); // 결과 : 익명 돼지
}

이렇게 클래스를 직접 상속 받아서, getName() 을 오버라이딩 해서 사용하는 방법이 있다. 그러면 이런 생각을 할 수 있다. "야 지금 이거 상속이고 뭐고, 그냥 pet 클래스 아니야?" 일단 이 질문에는 아니다. 언뜻보면, 그냥 Pet 클래스를 선언에서 오버라이드 좀 한걸로 볼 수 있지만 그렇지 않다.

🙉 Pet 익명클래스는 Pet 클래스가 아니다. 간단한 확인

🙈 생성된 인스턴스의 클래스 이름 확인 'instance.getClass().getName'

public static void main(String[] args) {
    Pet pet = new Pet();
    Pet pet2 = new Pet();
    System.out.println(pet.getClass().getName()); // 결과 Pet
    System.out.println(pet2.getClass().getName()); // 결과 Pet
}
  • 이 클래스들 pet 과 pet2는 일반적인 방법으로 객체를 생성한 것이다. 그럼 이 클래스의 이름은 뭐라고 뜰까?

🙈 생성된 인스턴스의 클래스 이름 확인 'instance.getClass().getName'

public static void main(String[] args) {
    Pet pet = new Pet(){ //익명 클래스 1개 선언
        //@Override 
    };
    Pet pet2 = new Pet(){ //익명 클래스 2개 선언
        //@Override 
    };
    System.out.println(pet.getClass().getName()); // 결과 Pet$1
    System.out.println(pet2.getClass().getName()); // 결과 Pet$2
}

결과 값이 신기하지 않은가? Pet + $ + n(생성된 몇번 째) 이런식의 클래스 이름이 나온다. 그러니깐 결론은, 자바에서는 이 서로 두개의 클래스를 같은 클래스로 보고 있지 않다는 것이다.

🙈 인터페이스로 선언된 익명 클래스는 현재 main에 속해있는 클래스 이름을 반환

public interface Monster {
    String getName();
}

public static void main(String[] args) {
    Monster mon = new Monster(){ //익명 클래스 1개 선언
        //@Override 
    };
    Moster mon2 = new Moster(){ //익명 클래스 2개 선언
        //@Override 
    };
    System.out.println(mon.getClass().getName()); // 결과 Pet$1
    System.out.println(mon2.getClass().getName()); // 결과 Pet$2
}

현재 클래스로 구현되어있기 때문에 지금 속해있는 main 클래스의 클래스 명을 반환한다.