Java 에서 두 객체가 같은지 확인하는 방법

JavaBeginner
지금 연습하기

소개

이 랩에서는 Java 에서 두 객체가 같은지 확인하는 방법을 배우게 됩니다. 객체 비교를 위해 == 연산자와 equals() 메서드를 사용하는 것의 근본적인 차이점을 살펴볼 것입니다.

먼저 내장된 equals() 메서드를 사용하여 객체를 비교하고 다양한 데이터 유형에서 해당 동작을 이해합니다. 그런 다음, 객체에 대한 논리적 동일성을 정의하기 위해 사용자 정의 클래스에서 equals() 메서드를 재정의하는 방법을 배우게 됩니다. 마지막으로, NullPointerException 오류를 방지하기 위해 동일성 검사를 수행할 때 null 객체를 처리하는 중요한 고려 사항을 다룰 것입니다.

equals() 메서드를 사용하여 동일성 확인

이 단계에서는 equals() 메서드를 사용하여 Java 에서 객체를 비교하는 방법을 살펴보겠습니다. == 연산자는 두 객체 참조가 메모리에서 정확히 동일한 객체를 가리키는지 확인하는 반면, equals() 메서드는 두 객체가 논리적으로 같은지, 즉 동일한 값이나 상태를 나타내는지 확인하도록 설계되었습니다.

이 개념을 시연하기 위해 간단한 Java 파일을 만들어 보겠습니다.

  1. WebIDE 를 열고 ~/project 디렉토리에 있는지 확인합니다. 터미널 프롬프트를 보거나 pwd를 입력하고 Enter 키를 눌러 확인할 수 있습니다.

  2. ~/project 디렉토리에 EqualityDemo.java라는 새 파일을 만듭니다. 왼쪽의 파일 탐색기에서 마우스 오른쪽 버튼을 클릭하고 "New File"을 선택한 다음 EqualityDemo.java를 입력하여 이 작업을 수행할 수 있습니다.

  3. 편집기에서 EqualityDemo.java 파일을 열고 다음 코드를 붙여넣습니다.

    public class EqualityDemo {
        public static void main(String[] args) {
            String str1 = new String("hello");
            String str2 = new String("hello");
            String str3 = str1;
    
            System.out.println("Comparing String objects:");
            System.out.println("str1 == str2: " + (str1 == str2));
            System.out.println("str1.equals(str2): " + str1.equals(str2));
            System.out.println("str1 == str3: " + (str1 == str3));
            System.out.println("str1.equals(str3): " + str1.equals(str3));
    
            System.out.println("\nComparing primitive types (int):");
            int num1 = 10;
            int num2 = 10;
            System.out.println("num1 == num2: " + (num1 == num2));
        }
    }
    

    이 코드에서:

    • 동일한 내용 ("hello") 을 가진 두 개의 String 객체 str1str2를 생성하지만, new String()을 사용하여 메모리에 별도의 객체를 생성합니다.
    • 세 번째 String 참조 str3을 생성하고 str1과 동일한 객체를 가리키도록 합니다.
    • str1str2, 그리고 str1str3을 비교하기 위해 ==equals()를 모두 사용합니다.
    • 또한 ==를 사용하여 기본 int 유형의 비교를 보여줍니다. equals()는 기본 유형이 아닌 객체에 사용된다는 점을 기억하십시오.
  4. EqualityDemo.java 파일을 저장합니다 (Ctrl+S 또는 Cmd+S).

  5. WebIDE 하단에서 터미널을 엽니다.

  6. 다음 명령을 입력하고 Enter 키를 눌러 Java 프로그램을 컴파일합니다.

    javac EqualityDemo.java
    

    오류가 없으면 출력이 표시되지 않습니다.

  7. 다음 명령을 입력하고 Enter 키를 눌러 컴파일된 프로그램을 실행합니다.

    java EqualityDemo
    

    다음과 유사한 출력이 표시됩니다.

    Comparing String objects:
    str1 == str2: false
    str1.equals(str2): true
    str1 == str3: true
    str1.equals(str3): true
    
    Comparing primitive types (int):
    num1 == num2: true
    

    str1 == str2는 내용이 같더라도 메모리에서 다른 객체이므로 false입니다. 그러나 str1.equals(str2)String 클래스의 equals() 메서드가 문자열의 실제 내용을 비교하도록 재정의되었기 때문에 true입니다. str1 == str3str3str1과 정확히 동일한 객체를 가리키기 때문에 true입니다.

    이것은 Java 에서 객체를 비교할 때 ==(참조 동일성) 와 equals()(논리적 동일성) 의 주요 차이점을 보여줍니다. 기본 유형의 경우 ==는 값 비교에 사용됩니다.

사용자 정의 클래스에서 equals() 재정의

이전 단계에서 String 객체에 대한 equals() 메서드의 작동 방식을 살펴보았습니다. String 클래스는 내용에 따라 의미 있는 비교를 제공하기 위해 기본 equals() 메서드 (Object 클래스에서 상속됨) 를 이미 재정의했습니다.

그러나 사용자 정의 클래스를 만들 때 Object에서 상속된 기본 equals() 메서드는 단순히 == 연산자를 사용하므로 참조 동일성만 확인합니다. 사용자 정의 클래스의 객체를 해당 속성 (논리적 동일성) 을 기반으로 비교하려면 equals() 메서드를 직접 재정의해야 합니다.

이 단계에서는 간단한 Person 클래스를 만들고 해당 equals() 메서드를 재정의합니다.

  1. WebIDE 에서 ~/project 디렉토리에 있는지 확인합니다.

  2. ~/project 디렉토리에 Person.java라는 새 파일을 만듭니다.

  3. Person.java를 열고 Person 클래스에 대한 다음 코드를 붙여넣습니다.

    public class Person {
        private String name;
        private int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
    
        // Default equals() method (inherited from Object) would only check reference equality
        // We need to override it to check for logical equality based on name and age
        @Override
        public boolean equals(Object obj) {
            // Step 1: Check if the objects are the same instance
            if (this == obj) {
                return true;
            }
    
            // Step 2: Check if the object is null or of a different class
            if (obj == null || getClass() != obj.getClass()) {
                return false;
            }
    
            // Step 3: Cast the object to the correct type
            Person person = (Person) obj;
    
            // Step 4: Compare the relevant attributes
            return age == person.age &&
                   name.equals(person.name); // Use equals() for String comparison
        }
    }
    

    Person 클래스에서:

    • name(String) 과 age(int) 의 두 가지 개인 속성이 있습니다.
    • 이러한 속성을 초기화하는 생성자가 있습니다.
    • 속성에 액세스하기 위한 getter 메서드가 있습니다.
    • equals() 메서드를 재정의했습니다. 재정의된 equals() 내부의 단계를 살펴보겠습니다.
      • if (this == obj): 이는 최적화입니다. 두 참조가 정확히 동일한 객체를 가리키는 경우, 확실히 같습니다.
      • if (obj == null || getClass() != obj.getClass()): 비교할 객체가 null 인지 또는 Person 클래스의 인스턴스가 아닌지 확인합니다. 둘 중 하나라도 참이면 같을 수 없습니다.
      • Person person = (Person) obj;: 일반 ObjectPerson 객체로 캐스팅하여 nameage 속성에 액세스할 수 있습니다.
      • return age == person.age && name.equals(person.name);: 이는 논리적 비교의 핵심입니다. age가 동일한지 (기본 int 의 경우 == 사용) 와 name이 동일한지 (String 객체의 경우 equals() 사용) 확인합니다.
  4. Person.java 파일을 저장합니다.

  5. 이제 재정의된 equals() 메서드를 테스트하기 위해 PersonEqualityDemo.java라는 다른 파일을 만들어 보겠습니다. ~/project 디렉토리에 이 파일을 만듭니다.

  6. PersonEqualityDemo.java를 열고 다음 코드를 붙여넣습니다.

    public class PersonEqualityDemo {
        public static void main(String[] args) {
            Person person1 = new Person("Alice", 30);
            Person person2 = new Person("Alice", 30);
            Person person3 = new Person("Bob", 25);
            Person person4 = person1;
    
            System.out.println("Comparing Person objects:");
            System.out.println("person1 == person2: " + (person1 == person2));
            System.out.println("person1.equals(person2): " + person1.equals(person2));
            System.out.println("person1 == person3: " + (person1 == person3));
            System.out.println("person1.equals(person3): " + person1.equals(person3));
            System.out.println("person1 == person4: " + (person1 == person4));
            System.out.println("person1.equals(person4): " + person1.equals(person4));
        }
    }
    

    이 데모 클래스에서는 여러 Person 객체를 만들고 ==와 재정의된 equals() 메서드를 사용하여 비교합니다.

  7. PersonEqualityDemo.java 파일을 저장합니다.

  8. 터미널을 엽니다. ~/project에 있는지 확인합니다.

  9. 두 Java 파일을 모두 컴파일합니다. 한 번에 여러 파일을 컴파일할 수 있습니다.

    javac Person.java PersonEqualityDemo.java
    

    그러면 Person.classPersonEqualityDemo.class 파일이 생성됩니다.

  10. 데모 프로그램을 실행합니다.

    java PersonEqualityDemo
    

    다음과 유사한 출력이 표시됩니다.

    Comparing Person objects:
    person1 == person2: false
    person1.equals(person2): true
    person1 == person3: false
    person1.equals(person3): false
    person1 == person4: true
    person1.equals(person4): true
    

    예상대로 person1 == person2는 서로 다른 객체이므로 false이지만, 재정의된 메서드에 따라 nameage가 같으므로 person1.equals(person2)true입니다. person1person3은 두 비교 모두에서 같지 않습니다. person1person4는 동일한 객체를 참조하므로 두 비교 모두에서 같습니다.

    equals() 메서드를 재정의하면 메모리 위치뿐만 아니라 논리적 상태를 기반으로 사용자 정의 클래스의 객체에 대한 "같음"의 의미를 정의합니다.

동등성 비교 시 Null 객체 처리

이전 단계에서 Person 클래스에서 equals() 메서드를 성공적으로 재정의하여 속성을 기반으로 객체를 비교했습니다. 견고한 equals() 메서드를 작성하는 데 중요한 측면 중 하나는 잠재적인 null 값을 처리하는 것입니다. null 객체에서 메서드를 호출하려고 하면 Java 에서 흔히 발생하는 오류인 NullPointerException이 발생합니다.

Person.java에서 재정의된 equals() 메서드는 이미 null에 대한 검사를 포함하고 있습니다: if (obj == null || getClass() != obj.getClass()). 이는 비교할 객체가 null인 경우를 처리하는 표준 방법입니다.

이 단계에서는 객체를 null과 비교할 때 어떤 일이 발생하는지 시연하고 equals() 메서드가 이를 올바르게 처리하는지 확인합니다.

  1. WebIDE 에서 ~/project 디렉토리에 있는지 확인합니다.

  2. 이전 단계에서 생성한 PersonEqualityDemo.java 파일을 엽니다.

  3. 기존 비교 문 뒤에 다음 줄을 main 메서드에 추가합니다.

    System.out.println("\nComparing with null:");
    System.out.println("person1.equals(null): " + person1.equals(null));
    

    이 코드는 단순히 person1null의 비교를 추가합니다.

  4. PersonEqualityDemo.java 파일을 저장합니다.

  5. 터미널을 엽니다. ~/project에 있는지 확인합니다.

  6. 수정된 PersonEqualityDemo.java 파일을 컴파일합니다.

    javac PersonEqualityDemo.java
    

    변경한 파일만 다시 컴파일하면 된다는 점을 기억하십시오. 이 단계에서 Person.java는 변경되지 않았으므로 PersonEqualityDemo.java만 컴파일하면 됩니다.

  7. 컴파일된 프로그램을 실행합니다.

    java PersonEqualityDemo
    

    이제 이전 출력 다음에 null 과의 새로운 비교가 표시됩니다.

    Comparing Person objects:
    person1 == person2: false
    person1.equals(person2): true
    person1 == person3: false
    person1.equals(person3): false
    person1 == person4: true
    person1.equals(person4): true
    
    Comparing with null:
    person1.equals(null): false
    

    출력 person1.equals(null): false는 재정의된 equals() 메서드가 null과의 비교를 올바르게 처리하고 NullPointerException을 발생시키지 않고 false를 반환함을 확인합니다. 이는 Person 클래스의 equals() 메서드에서 if (obj == null || getClass() != obj.getClass()) 줄이 obj의 속성에 액세스하기 전에 null을 확인하기 때문입니다.

    null을 처리하는 것은 Java 에서 견고한 코드를 작성하는 데 중요한 부분이며, 특히 객체 비교를 처리할 때 더욱 그렇습니다. 재정의된 equals() 메서드의 시작 부분에 항상 null 검사를 포함하십시오.

요약

이 Lab 에서는 Java 에서 두 객체가 같은지 확인하는 방법을 배웠습니다. 참조 동일성을 확인하는 == 연산자와 논리적 동일성을 확인하는 equals() 메서드의 차이점을 이해하는 것으로 시작했습니다. String 객체와 기본 유형을 사용하여 이를 시연하고 객체에 대해 ==equals()와 어떻게 다르게 동작하는지 관찰했습니다.

그런 다음 객체 동일성에 대한 자체 기준을 정의하기 위해 사용자 정의 클래스에서 equals() 메서드를 재정의하는 방법을 살펴보았습니다. 이는 사용자 정의 객체가 메모리 위치가 아닌 내용 또는 상태를 기반으로 비교되도록 보장하는 데 중요합니다. 마지막으로, NullPointerException을 방지하고 강력한 동일성 검사를 보장하기 위해 equals() 메서드 내에서 null 객체를 처리하는 것의 중요성을 배웠습니다.