JAVA의 가장 기본이 되는 내용

java.lang 패키지와 그 첫번째 - Object

cwchoiit 2024. 3. 31. 11:48
728x90
반응형
SMALL

참고 자료:

 

김영한의 실전 자바 - 중급 1편 | 김영한 - 인프런

김영한 | 실무에 필요한 자바의 다양한 중급 기능을 예제 코드로 깊이있게 학습합니다., [사진]국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바

www.inflearn.com

패키지 중 java.lang이라는 패키지가 있다. 자바가 기본으로 제공하는 라이브러리(클래스 모음) 중에 가장 기본이 되는 것이 바로 java.lang 패키지이다. 여기서 lang은 Language의 줄임말이다. 

 

java.lang 패키지의 대표적인 클래스들

  • Object: 모든 자바 객체의 최상위 부모 클래스
  • String: 문자열
  • Integer, Long, Double: Wrapper 타입, 기본형 데이터 타입을 객체로 만든 것
  • Class: 클래스 메타 정보
  • System: 시스템과 관련된 기본 기능들을 제공

이 클래스들은 너무나 기본이 되는 내용들이고 중요하기 때문에 반드시 잘 알아두어야 한다.

 

import 생략 가능

java.lang 패키지는 모든 자바 애플리케이션에 자동으로 임포트가 된다. 따라서 임포트 구문을 사용하지 않아도 된다.

다음과 같이 임포트 없이 사용할 수 있다.

 

Object 클래스

모든 객체의 최상위에는 항상 Object 클래스가 있다. "어? 저는 Object를 상속받은 클래스가 없는데요?" 자바가 알아서 해준다.

그러니까 extends 키워드로 특정 클래스를 상속받지 않는 클래스는 자바가 암묵적으로 extends Object를 넣어준다.

 

Object 클래스가 필요한 이유

그럼 왜 이 Object 클래스가 필요한 걸까? Object 클래스에는 다음과 같은 대표적인 메서드가 있다.

  • toString()
  • getClass()
  • equals()

이는 객체가 어떤 객체인지 알기 위해 반드시 필요한 기능이고 있어야만 한다. 근데 만약 Object라는 클래스가 없으면 개발자마다 저 기능들을 본인 입맛에 맞게 구현할 텐데 그때마다 다 시그니쳐가 다를 거다. 예를 들어, 객체끼리 서로 같은 객체인지 비교하기 위한 메서드인 equals()라는 메서드를 누군가는 same()이라는 메서드로 만들 수 있을 것이다. 이렇게 반드시 객체라면 필요한 기능을 규칙 없이 개발자마다 달라지는 게 아니라 자바가 딱 하나로 정의를 해두고 그것을 약속하면 서로 다른 개발자들끼리 혼동이 없을 것이다. 이 때문에 Object라는 클래스가 존재한다. 

 

그리고 한가지 더 Object라는 클래스가 존재하는 이유는 객체 지향의 꽃인 다형성의 시작을 내포하기 때문이다. Object는 모든 객체의 결국 최상위 부모이다. 그 말은 어떤 객체를 만들어도 Object라는 타입으로 담을 수 있다는 얘기다. 객체와 메모리 구조를 잘 떠올려보라.

어떤 객체를 새로 만들어도 위 그림과 같이 결국 최상위에는 Object가 있기 때문에 어떤 객체를 만들어도 Object로 업캐스팅이 가능하다. 즉, 시작부터 다형성인 것이다. 따라서 타입이 다른 객체들을 어딘가에 보관해야 한다면 바로 Object에 보관하면 된다.

 

이 말을 코드로써 표현해보자. 예를 들어, 배열이 있다고 생각해 보자. 근데 그 배열에는 여러 타입을 담고 싶은 것이다. 보통의 배열이라면 특정 타입으로 선언해서 해당 타입에 맞는 요소들만 담을 수 있는데 그게 아니고 이 타입 저 타입 모두 한 배열에 담고 싶은 요구사항이 있는 것이다. 이때 사용할 수 있는 게 Object인 것이다.

 

다음 코드를 보자. 전혀 다른 타입의 요소들이 하나의 배열에 들어가 있다. 이것이 바로 Object라는 클래스가 주는 다형성이다.

 

어떤 타입이던 Object는 그 상위에 있는 부모이기 때문에 담을 수가 있다. 만약, Object가 없다면 이러한 행위가 불가능하다. 물론, MyObject라는 클래스를 직접 개발자가 정의해서 모든 클래스마다 이 MyObject를 상속받으면 담을 수 있겠지만 그건 그 코드한정이다. 다른 개발자가 만든 코드에선 절대 호환되지 않을 것이다. 다른 개발자가 만든 코드에는 MyObject가 없을 테니까. 이게 Object 클래스가 있는 이유이다.

 

Object와 OCP

OCP 원칙을 기억하는가? Open-Closed Principle 즉, 확장에는 열려있고 변경에는 닫혀있는 이 원칙이 Object에도 이미 적용되어 있다. 이 원칙에 가장 대표적인 예시가 System.out.println()Object이다. 우리가 알고 그냥 썼던 System.out.println()은 파라미터로 Object 타입을 받는다. 그리고 실제 그 내부 코드를 들어가 보면 파라미터로 받는 ObjecttoString() 메서드를 호출한다는 사실을 아는가?

 

System.out.println()은 다형성의 끝판왕이다.

 

Object라는 가장 상위 부모인 클래스를 파라미터로 받아 다형적 참조가 가능하게 했고, toString()이라는 메서드를 호출할 때 어떤 인스턴스가 들어오던 toString()을 오버라이딩 했다면 그 오버라이딩한 메서드를 호출하고 그게 아니라면 부모가 가지고 있는 toString()이라는 메서드를 호출해서 메서드 오버라이딩 기능을 제대로 사용하고 있는 것이다.

 

그리고 그 결과 아무리 많은 클래스를 만들어도 그 클래스의 부모는 Object이기 때문에 확장에 무한히 열려있다(Open). 그리고 이렇게 확장을 원하는 대로 하더라도 사용하는 클라이언트 코드인 System.out.println()에는 어떠한 변경도 필요가 없다(Closed). 

 

완벽한 OCP 원칙이라고 볼 수 있다.

 

 

equals()

Object는 동등성 비교를 위한 equals() 메서드를 제공한다.

 

자바는 두 객체가 같다는 표현을 2가지로 분리해서 제공한다.

  • 동일성(Identity): == 연산자를 사용해서 두 객체의 참조가 동일한 객체를 가리키고 있는지 확인
  • 동등성(Equality): equals() 메서드를 사용해서 두 객체가 논리적으로 동등한 지 확인

동일은 완전히 같음을 의미한다. 반면, 동등은 같은 가치나 수준을 의미하지만 그 형태나 외관 등이 완전히 같지는 않을 수 있다.

쉽게 이야기해서 동일성은 물리적으로 같은 메모리에 있는 객체 인스턴스인지 참조값을 확인하는 것이고, 동등성은 논리적으로 같은지 확인하는 것이다. 동일성은 자바 머신 기준이고 메모리의 참조가 기준이므로 물리적이다. 반면 동등성은 보통 사람이 생각하는 논리적인 기준에 맞추어 비교한다. 

 

예를 들어, 같은 회원 번호를 가진 회원 객체가 2개 있다고 가정해 보자.

User user1 = new User("id-100");
User user2 = new User("id-100");

 

이 경우, 물리적으로 다른 메모리에 있는 다른 객체이지만, 회원 번호를 기준으로 생각해 보면 논리적으로는 같은 회원으로 볼 수 있다.

그 말은 동일성은 다르지만 동등성은 같다. 

 

문자의 경우도 생각해 보자.

String s1 = "hello";
String s2 = "hello";

이 경우 물리적으로는 각각의 "hello" 문자열이 다른 메모리에 존재할 수 있지만, 논리적으로는 같은 "hello"라는 문자열이다.

(사실 이 경우 자바가 같은 메모리를 사용하도록 최적화한다. 그래서 == 비교를 해도 '참'을 반환한다.)

 

동일성과 동등성 비교

예제를 통해서 동일성과 동등성을 비교해 보자.

 

어떤 결과가 나올 것 같은가? 우선 '==' 연산자를 사용한 경우 동일성, 즉 같은 메모리에 들어있는가?를 묻고 있는 것이기 때문에 거짓이 될 것이다. 그럼 동등성을 비교하는 equals()를 사용했을 때 두 User 객체가 같은 ID를 사용하므로 참을 반환할까? 결과는 다음과 같다.

 

실행결과:

Identity = false
Equality = false

 

'==' 연산자는 납득이 되는데 equals()를 사용했을 때도 false가 나왔다. 이 이유는 기본적으로 Object 클래스에 있는 equals() 메서드는 다음과 같이 생겼다. 즉, 똑같이 '==' 비교를 하고 있다.

 

엇? 왜 이럴까? 생각해 보면 모든 클래스는 다 동등성을 비교하는 기준이 다를 것이다. 어떤 클래스는 ID로, 어떤 클래스는 Name으로, 어떤 클래스는 그 두 개를 동시에 비교해서 같은지를 판단할 것이다. 즉, 누가 어떻게 어떤 의도로 만들었냐에 따라 동등성은 다 달라질 것이다라는 말이다. 그걸 Object라는 하나의 클래스가 모든 케이스를 다 정의할 수 없다. 그래서, 이 equals() 메서드를 사용자가 재정의(오버라이딩) 해야 한다. 그렇지 않으면 기본으로 Object 클래스는 eqauls() 메서드 내부에서 동일성 비교를 한다.

 

그럼 직접 equals() 메서드를 오버라이딩 해보자. 근데, 그럴 필요가 없다. 왜냐? IDE에서 잘 알아서 해준다. 그래서 우린 IDE의 도움을 받으면 된다. 어떻게 하냐? 맥 기준 커맨드 + N 을 눌러보면 다음과 같이 나온다.

여기어 보이는것과 같이 equals() and hashCode()를 선택하면 된다. 그럼 여러 선택 팝업이 나오는데 일단 이 부분은 Next를 클릭한다.

 

그리고 그 다음 나오는 부분이 중요하다면 중요할 수 있는데, 어떤 필드를 기준으로 동등성을 체크할지를 묻는 팝업이다. 지금은 id가 같으면 동등하다고 볼 것이므로 다음과 같이 ID를 체크하고 넘어가면 된다.

 

그래서 딱히 변경할 것 없이 다 Next 하고 Create 하면 다음과 같이 이쁜 equals()를 만들어준다.

 

코드를 하나씩 뜯어보자.

1. 처음에 '==' 비교가 있다. 즉, 참조값 자체가 같으면 동등을 떠나 동일하다는 거니까 바로 참을 반환한다.

2. 어떤 객체가 null인 경우 반드시 그건 동일하지 않아야 하며, 혹여나 현재 클래스와 다른 인스턴스 타입이라면 그것도 또한 동일하지 않다. 그래서 그 조건 중 하나라도 참이라면 거짓을 반환한다. 

3. 위 조건을 만족해야만 현재 클래스로 다운 캐스팅이 가능하다.

4. 체크한 id를 기반으로 this.id와 파라미터로 넘겨받은 객체의 id가 같다면 참, 다르다면 거짓을 반환한다.

 

 

 

728x90
반응형
LIST

'JAVA의 가장 기본이 되는 내용' 카테고리의 다른 글

String 클래스  (0) 2024.04.01
불변객체 (Immutable Object)  (2) 2024.04.01
OCP (Open-Closed Principle) 원칙  (0) 2024.03.30
다형성 (Part.2) 사용하기 ✨  (0) 2024.03.29
다형성 (Part.1) 매우중요 ✨  (0) 2024.03.28