[JAVA] 12-4. 표준 제공 라이브러리 클래스와 Object 클래스

Re_Go·2024년 5월 28일
0

JAVA

목록 보기
20/37
post-thumbnail

1. 표준 제공 라이브러리 클래스들

앞서 말씀드린 대로 자바에서는 사용자들에게 표준 라이브러리들을 제공하는데, 우리는 이 라이브러리에 수록되어 있는 클래스들로 보다 유용한 코드 작성이 가능합니다.

이러한 자바에서 기본적으로 제공해주는 라이브러리들은 해당 사이트에서 검색이 가능하며,스크롤을 내리다보면 All Classes and Interfaces 탭이 있는데, 거기서 자바에서 제공하는 모든 표준 라이브러리(인터페이스 및 열거, 예외 클래스 및 어노테이션 포함)들을 확인하실 수 있습니다.

해당 탭에서 특정 클래스 및 인터페이스를 클릭하면 상세 내용이 나오는데, 해당 객체가 구체적으로 제공하는 중첩 요소나 필드, 생성자 및 메서드를 확인하려면 화면 상단에 SUMMARY 부분에 활성화된 링크를 들어가면 각 요소들을 확인하실 수 있습니다.

특정 클래스를 클릭 시 상세한 설명이 출력되며

SUMMARY에 나와있는 각 구성 멤버들을 클릭하면 자세한 설명을 확인할 수 있습니다.

참고로 사용자들이 자주 사용하는 클래스들은 거의 java.base 모듈java.lang 패키지에 저장되어 있으며, 해당 클래스의 메서드들을 상속 받는 클래스들은 표준 라이브러리들의 클래스의 메서드를 오버라이드 하여 재정의 하는 것 또한 가능합니다.

2. Object 클래스와 메서드

자바스크립트에서는 클래스를 포함한 모든 객체가 생성시에 기본적으로 Object 클래스의 프로토타입을 상속 받습니다.

이후에는 프로토타입 체인 상에 연결된 관계(상속 상태)에 따라 체이닝을 끊거나 연결할 수 있는데요.

자바에서는 자바스크립트처럼 프로토타입 체이닝 같은 상속 관계를 나타내는 시스팀에 존재하지는 않지만, 기본적으로 모든 객체(클래스, 인터페이스를 포함)는 java.lang.Object 클래스를 상속받게 됩니다.

equals

이러한 Object 클래스의 대표적인 메서드는 equals() 인데요. 이 메서드는 두 비교 대상의 내용이 동등한 위치에 있는지를 비교할 때 사용하는 메서드로서 비교 연산자 (==) 와 같은 역할을 하는데요.

차이점이라고 한다면 동등 비교 연산자는 두 비교 대상의 주소가 같은지를 확인하는 연산자로서, 주로 기본 타입 변수의 주소 비교에 사용되며, 객체의 내용이 같은지를 확인하기 위해서는 equals 연산자를 사용해주면 됩니다.

예시 ) 매개변수의 객체가 타입이 같고 id의 내용까지 같은지를 확인하는 메서드

public class Member{
	//기본 필드
	public String id;

	// 생성자
    public Member(String id) {
    this.id = id;
    }

    @oOverride 
    public boolean equals(Object obj){
    	if(obj instanceof Member target) { // 만약 매개변수의 obj가 Member의 인스턴스라면 Member 타입으로 변환 후 target 변수에 저장
        	if(id.equals(target.id)) return true;
        }
        return false; // id가 맞지 않는 경우 false를 반환
    }
}

위의 코드에서 좀 더 구체적으로 정의를 해보자면, 모든 클래스는 Object 클래스를 상속 받으므로 이 equals 메서드 또한 오버라이딩 하는 것이 가능하기에, 오버라이딩을 받아 해당 클래스로 생성한 인스턴스들 간에 전체 값이나 특정 값을 비교하는 용도로도 커스터마이징 하는 것도 가능하죠.

클래스의 equals 재정의

class Computer{
	private String model;
	private int price;
	public Computer(String model, int price) {
		this.model = model;
		this.price = price;
	}

	// Object의 equals 메서드를 재정의한 메서드, 어떤 타입이 들어오더라도 비교할 수 있도록 매개변수의 타입을 Obj로 지정해주고 매개변수가 들어오면 Object obj = 하위 클래스의 인스턴스 형식으로 업캐스팅 됩니다.
	@Override
	public boolean equals(Object obj) {
    	// obj는 현재 업캐스팅이 된 상태이므로, 현재 인스턴스의 타입(Computer)인지부터 확인이 필요한데, 이를 위해서 매개변수의 값이 null이 아니고, Computer의 인스턴스 라고 할 때 비교가 이루어지도록 조건을 만듭니다.
		if(obj != null && obj instanceof Computer) {
        // 조건이 맞다면, 즉 매개변수의 타입이 현재 equals를 호출한 인스턴스의 타입과 맞다면 매개변수의 인스턴스를 Computer 타입으로 다운 캐스팅 해줍니다.
			Computer another = (Computer) obj;
            // equals 메서드를 호출한 현재 Computer 인스턴스의 필드 값과 매개변수로 넘어와 조건에 의해 다운캐스팅 된 또 다른 Computer 인스턴스의 필드 값이 같다면 (문자열은 equals로, 숫자형은 등호로 비교) true를 반환하고, 둘 중 하나라도 틀릴 경우 false를 반환
			return model.equals(another.model) && price == another.price;
		}else return false;
	}
}

실행 코드

	public static void main(String[] args) {
		Computer com1 = new Computer("삼성", 100);
		Computer com2 = new Computer("삼성", 100);
		// com1에서 재정의한 equals 메서드로 com2와 내용이 같은지를 비교하여 true를 반환 받음
		System.out.println(com1.equals(com2));
	}

hashCode

다음으로 살펴볼 메서드는 hashCode인데요. hashCode는 일종의 멤버 요소에 대한 고유 번호를 의미하며, 이 정보를 토대로 해시 테이블 기반의 컬렉션에서 객체를 신속히 검색하는데 활용됩니다. 그리고 이 메서드를 재정의 할 경우 필드 멤버들의 정보를 토대로 재생성하도록 함으로서 객체마다 내용이 같은지를 확인하게 할 수도 있습니다.

public class Member{
	//기본 필드
	public String id;

	// 생성자
    public Member(String id) {
    this.id = id;
    }

    // Object 클래스로부터 상속 받는 hashCode 메서드를 재정의.
    @Override
    public int hashCode(){
    	return id.hashCode();
    }
}

toString

마지막으로 toString 메서드는 객체의 상태를 확인하는데 사용되는데요. 해시 기반 컬렉션에서의 검색에 사용되는 hashCode의 목적과는 다르게 사용되는데, 문제는 toString으로 반환되는 정보가 육안으로는 식별하기 힘든 정보라는 것입니다.

그래서 대부분은 toString 메서드를 재정의해서 해당 필드의 멤버들의 정보를 보기 쉽게 반환하는데 주로 사용됩니다. 즉 해당 타입의 인스턴스를 생성하고, 굳이 toString메서드를 호출하지 않아도 그냥 인스턴스 명만 써주면 toString이 자동으로 호출되어 우리가 재정의한 내용들을 확인할 수 있다는 것이죠.

클래스의 toString 재정의

class Member{
	//기본 필드
	private String name;
	private int no;
	// 생성자
    public Member(String name, int no) {
    	this.no = no;
    	this.name = name;
    }

    @Override
    public String toString(){
    	return "이름 : " + this.name + "\n" + "NO : " + this.no;
    }
}

실행 코드

public static void main(String[] args) {
		Member member = new Member("Re_Go", 10);
        // 이름 : Re_Go , NO : 10 출력
		System.out.println(member);
}

3. DTO와 record 선언

자바에서는 데이터 전송을 위해 DTO(Data Transfer Object)라는 개념을 가지고 있는데요. 이는 다양한 계층 간 데이터 전달의 효율성을 목적으로 극대화 하기 위해 사용되는 클래스 틀을 의미하는데요.

보안 및 매핑 목적으로 자주 사용되기도 하는 이 DTO의 골자는 다음 코드와 같습니다.

public class MemberDTO {
	// 상수 필드 선언
    private final String name;
    private final int age;
	
	// 생성자 생성
    public MemberDTO(String name, int age) {
        this.name = name;
        this.age = age;
    }

	//getter 메서드 설정 (setter는 보안 목적으로 사용 x)
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

	// 상속받은 Object 클래스의 toString 메서드 재정의
    @Override
    public String toString() {
        return "MemberDTO{name='" + name + "', age=" + age + "}";
    }

	// 상속받은 Object 클래스의 hashCode 메서드 재정의
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

	// 상속받은 Object 클래스의 equals 메서드 재정의
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        MemberDTO memberDTO = (MemberDTO) obj;
        return age == memberDTO.age && Objects.equals(name, memberDTO.name);
    }

    public static void main(String[] args) {
        MemberDTO member1 = new MemberDTO("Re_Go", 30);
        MemberDTO member2 = new MemberDTO("Re-Go", 29);

        // getter 메서드 호출
        System.out.println(member1.getName());
        System.out.println(member1.getAge());
        // toString 메서드 호출
        System.out.println(member1.toString());
        // hashCode 메서드 호출
        System.out.println(member1.hashCode());
        // equals 메서드 호출
        System.out.println(member1.equals(member2));
    }
}

문제는 이러한 코드를 반복적으로 작성하는것은 비효율적이라는 것인데, 이를 지원하기 위해 Java 14부터 record 객체가 추가되었는데,

위에서 살펴본 DTO를 자동으로 추가해준다는 장점을 가지고 있어 매우 유용하게 사용되는 객체중 하나입니다.

// Member 레코드 생성
public record Member(String name, int age) {

	public static void main(String[] args) {
		Member member1 = new Member("Re_Go", 30);
		Member member2 = new Member("Re-Go", 29);
		
		// getter 메서드 호출 (접두사를 get으로 쓰지 않고 멤버 이름과 같게 자동 생성됩니다.)
		System.out.println(member1.name); 
		System.out.println(member1.age);
		// toString 메서드 호출
		System.out.println(member1.toString());
		// hashCode 메서드 호출
		System.out.println(member1.hashCode());
		// equals 메서드 호출
		System.out.println(member1.equals(member2));
	}
}

참고로 이 record 객체와 비슷한 지원을 해주는 롬복(Lombok) 이라는 커스텀 라이브러리가 있는데요. 해당 내용은 이곳을 참조하시기 바립니다.

profile
인생은 본인의 삶을 곱씹어보는 R과 타인의 삶을 배워 나아가는 L의 연속이다.

0개의 댓글