Post

면접준비 - Java 동등성, 동일성

오타, 지적 환영입니다.

A라는 객체와 B라는 객체가 있다고 치자.

한줄로 요약해서 동일성은 A와 B의 메모리 주소가 같은 경우이고, 동등성은 둘이 다른 메모리를 가리킬지라도 저장된 정보는 같은 경우를 말한다.

✍️동등성

1
2
3
4
5
6
7
    String A = new String("안녕하세요?");
    String B = A;
    System.out.println(A == B); // true
    //Primitive type
    int a = 1;
    int b = a;
    System.out.println(a == b); // true

A는 어딘가에 생성된 인스턴스(“안녕하세요?”)의 JVM내의 메모리 주소를 가리키고 있을 것이다.

B는 A를 가리키고 있으므로 둘의 메모리 주소는 같을 것이다. 고로 동등하다고 볼 수 있다.

동등하다는것을 코드로 확인하기 위해서는 A==B문구를 통해 확인할 수 있다.

원시타입의 경우는 가리키는 객체의 메모리 주소가 없으므로 값을 비교하여 같다면 동등하다고 판단한다.

1
2
3
4
// 내용이 같아도 가리키는 객체의 주소가 다르므로 
    String A = new String("안녕하세요?");
    String B = new String("안녕하세요?");
    System.out.println(A == B); // false

내용이 같아도 각 다른 인스턴스이므로 당연하게도 false가 출력된다.

동등성

가리키는 객체의 정보가 같다면 동등하다고 본다.

동일하다면 동등하다고 볼 수 있지만, 동등하다면 동일한것은 아니다

1
2
3
4
    String A = new String("안녕하세요?");
    String B = new String("안녕하세요?");
    System.out.println(A == B); // false
    System.out.println(A.equals(B)); // true

둘의 정보는 같기에 동등하다.

String객체의 경우는 내부적으로 다음과 같이 동작한다.

  • step1 동일성을 먼저 체크. 동등할경우 true리턴
1
2
3
4
5
6
7
8
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        return (anObject instanceof String aString) // < 이 부분
                && (!COMPACT_STRINGS || this.coder == aString.coder)
                && StringLatin1.equals(value, aString.value);
    }
  • step2 String객체인지 확인
1
2
3
return (anObject instanceof String aString) // < 이 부분
                && (!COMPACT_STRINGS || this.coder == aString.coder)
                && StringLatin1.equals(value, aString.value);
  • step3 배열의 요소를 돌면서 값이 같은지 확인
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
return (anObject instanceof String aString) 
                && (!COMPACT_STRINGS || this.coder == aString.coder) //<- 해석 불가.
                && StringLatin1.equals(value, aString.value); //<- 이 부분

//배열을 돌면서 요소 확인
@IntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
    if (value.length == other.length) {
        for (int i = 0; i < value.length; i++) {
            if (value[i] != other[i]) {
                return false;
            }
        }
        return true;
    }
    return false;
}

실제 String,Integer,ArrayList 모두 이해하는대로 잘 동작한다.

내부 코드에 대해서 설명이 더 필요하다면 이 글을 추천한다.

근데, 클래스의 경우는 어떻게 동등하다고 판단할까? 클래스의 경우 값이 같다고 동등하지 않았다.

클래스의 경우 동등하다?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Test {
    private int age;
    private String name;

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
//main이라는 뜻
psvm{
    Test classA = new Test();
    Test classB = new Test();

    classA.setAge(18);
    classA.setName("name");

    classB.setAge(18);
    classB.setName("name");

    System.out.println(classA.equals(classB)); //false
    System.out.println(classA.getName().equals(classB.getName())); //true
}

내용이 같다면 “동등하다”라고 했는데 클래스의 경우 그렇지 않았다.

요소들의 값이 동등하지만 클래스 자체는 동등하지 않다는 것이다.

왜 그럴까? 일단 클래스는 Object형이다. 그러면 Object에서 재정의된 equals()를 보면 답이 나온다.

1
2
3
4
//java 18
public boolean equals(Object obj) {
        return (this == obj);
    }

java 18 버전 기준 다음과 같이 위 코드를 해석하면 다음과 같이 정의된다. 동일하지 않다면 동등하지 않다.

Objectequals()에 대한 동등성이 없으므로 Object 자료형 같은 경우 equals()로 동등성을 비교하고 싶다면 오버라이딩 해줘야한다는 것이다.

위의 경우 Test 클래스 안에

1
2
3
4
@Override
    public boolean equals(Object obj) {
        return (this.getClass().getName().equals(obj.getClass().getName()));
    }

로 오버라이딩 해주면 true가 된다. 간단하게 클래스 이름만 동등한지 확인하는 코드다.

Reference

This post is licensed under CC BY 4.0 by the author.