article thumbnail image
Published 2020. 4. 30. 17:17

자바에서 상속이란 필수적이다. 여러분이 상속하건 하지 않았건 기본적인 상속을 하게 된다.

 

package org.opentutorials.javatutorials.progenitor;
 
class O {}

O라고 하는 클래스를 정의했다. 그리고 다른 클래스를 상속받고 있지 않다.

그러나 위의 코드는 아래와 코드가 같다.

 

package org.opentutorials.javatutorials.progenitor;
 
class O extends Object {}

자바에서 어떤 클래스가 다른 클래스를 상속받고 있지 않는다면 Object를 상속받게 한다.

자바에서 모든 클래스는 사실 Object를 암시적으로 상속받고 있는 것이다.

그런 점에서 Object는 모든 클래스의 조상이라고 할 수 있다.

그 이유는 모든 클래스가 공통으로 포함하고 있어야 하는 기능을 제공하기 위해서다.

 

Object가 갖는 메쏘드를 통해서
각각의 클래스들이 필수적으로 가지고 있어야할 공통적인 기능을
Object라는 클래스에 구현해서 제공한다.

 

API 문서를 보자.

http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html

메소드의 목록을 살펴보자.

 

위의 그림은 Object 클래스가 가지고 있는 메소드를 보여준다.

다시 말해서 자바의 객체는 위의 메소드들을 반드시 가지고 있다고 할 수 있다.

클래스를 하나 만들면 위의 기능들을 자동적으로 갖게 된다.

이 중에 중요하면서 입문 단계에서 이해할 수 있는 API들을 살펴보자.


 toString() 

객체를 문자화 시켰을 때 

toString은 객체를 문자로 표현하는 메소드이다. 기본 예제인 계산기 코드를 보자.

package org.opentutorials.javatutorials.progenitor;
 
class Calculator{
    int left, right;
      
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    }
    public void sum(){
        System.out.println(this.left+this.right);
    }
      
    public void avg(){
        System.out.println((this.left+this.right)/2);
    }
}
  
public class CalculatorDemo {
      
    public static void main(String[] args) {
          
        Calculator c1 = new Calculator();
        c1.setOprands(10, 20);
        System.out.println(c1);
    }
  
}

System.out.println은 클래스 Calculator의 인스턴스 c1을 화면에 출력하고 있다.

System.out.println(c1);

 

필자의 결과는 아래와 같다. @ 뒤의 내용은 각자 다를 것이다.

org.opentutorials.javatutorials.progenitor.Calculator@11be650f

패키지는 org.opentutorials.javatutorials.progenitor이고

Calculator라는 클래스 이다. 라는 뜻이다.

@ 는 아무 의미가 없는 앞의 것과 뒤의 것을 구분하기 위한 것.

뒤에있는 것은 인스턴스를 식별하기 위한 식별자이다.

 

System.out.println(c1.toString);

이렇게 써도 위의 코드와 똑같은 결과를 가져온다.

toString이라는 메쏘드를 호출하도록 약속이 되어있기 때문이다.

 

이것은 Calculator라는 클래스에도

class Calculator extends Object{}

Object 클래스의 상속을 생략하고 있기 때문이다.

Object 클래스 안에는 toString() 메쏘드가 있다.

 

toString()이라고 하는 것은 어떠한 객체가 있을 때 그 객체를 일종의 문자열로 표시해주는, 문자화 시키는 메쏘드이다는 것을 알 수 있다.

System.out.println(c1); 이라고 써도 출력되는 이유는 암시적으로 약속을 해놓았기 때문이다.


위의 정보도 유용한 정보이지만 클래스 설계자의 필요에 따라서 toString의 결과를 더욱 유용하게 만들 수 있다.

예를들어 계산기 인스턴스의 left, right 값을 알 수 있다면 개발을 좀 더 편하게 할 수 있을 것이다.

package org.opentutorials.javatutorials.progenitor;
 
class Calculator{
    int left, right;
      
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    }
    public void sum(){
        System.out.println(this.left+this.right);
    }
      
    public void avg(){
        System.out.println((this.left+this.right)/2);
    }
     
    public String toString(){
        return "left : " + this.left + ", right : "+ this.right;
    }
}
  
public class CalculatorDemo {
      
    public static void main(String[] args) {
          
        Calculator c1 = new Calculator();
        c1.setOprands(10, 20);
        System.out.println(c1);
        System.out.println(c1.toString());
    }
  
}

 

차이점

 

실행결과

 

클래스 Calculator에 toString을 재정의(overiding)했다.

그리고 인스턴스를 System.out.println의 인자로 전달하니까 toString을 명시적으로 호출하지 않았음에도 동일한 효과가 나고 있다.

 

toString 메소드는 자바에서 특별히 취급하는 메소드다.

toString을 직접 호출하지 않아도 어떤 객체를 System.out.print로 호출하면 자동으로 toString이 호출되도록 약속되어 있다.

이를 통해서 인스턴스 c1의 상태를 쉽게 파악할 수 있게 되었다.

 

이렇게 만든다면

org.opentutorials.javatutorials.progenitor.Calculator@11be650f, left:10, right:20

으로 나올 것이다.


 equals() 

equals는 객체와 객체가 같은 것인지를 비교하는 API이다.

객체 간에 같고 다름은 필요에 따라서 달라질 수 있기 때문이다.

 

package org.opentutorials.javatutorials.progenitor;
 
class Student{
    String name;
    Student(String name){
        this.name = name;
    }
}
 
class ObjectDemo {
 
    public static void main(String[] args) {
        Student s1 = new Student("egoing");
        Student s2 = new Student("egoing");
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
 
    }
 
}

s1이라는 객체와 s2라는 객체를 비교하고 있다.

결과는 

false

false 이다.

 

이것을 같게 나타내는 방법이 있다. 오버라이딩을 통한 방법이다.

 

equals 메쏘드는 매개변수는 Object 데이터 타입을 받고 있다.

자식 데이터타입은 부모 데이터타입에 할당될 수 있다.

그 일이 매개변수에서 일어나고 있다.

 

그런데 equals 메쏘드는 s2를 Object 데이터 타입으로 하게되면 

s2가 가지고 있는 변수인 name이라는 변수에 접근할 수 없게 된다.

 

왜냐?

Object 데이터 타입에는 name이라는 멤버가 존재하지 않기 때문이다.

그래서 Object 데이터 타입을 Student 데이터 타입으로 바꿔야 한다.

 

자식이 부모행세 하는건 가능하다.

	Object obj = s2;

 

그러나 부모가 자식행세 하는건 불가능하다.

        Student s = obj;

불가능 X

        Student s = (Student)obj;

가능 O

 

Object 데이터 타입을 Student 데이터 타입으로 형변환 한다.

명시적으로 형변환을 시켜줘야한다.

 

그러면 왜 Object 타입을 매개변수로 받느냐?
매겨변수로 어떤 타입이 들어올지 모르기 때문에

package org.opentutorials.javatutorials.progenitor;
 
class Student{
    String name;
    Student(String name){
        this.name = name;
    }
    public boolean equals(Object obj) { // 접근제어자 : public, return값 : boolean 
    					// 매개변수의 데이터 타입 : Object
        Student s = (Student)obj;
        return name == s.name;
    }
}
 
class ObjectDemo {
 
    public static void main(String[] args) {
        Student s1 = new Student("egoing");
        Student s2 = new Student("egoing");
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
 
    }
 
}

 

 

결과는 아래와 같다.

아래 코드를 보자.

System.out.println(s1 == s2);

 

결과는 false다.

 

그 이유는 s1과 s2가 서로 다른 객체이기 때문이다.

어찌 보면 당연한 결과다.

하지만 두 개의 객체가 논리적으로는 egoing이라는 값을 가지고 있기 때문에 객체를 만든 필자는 저 두 개의 객체가 같은 객체로 간주 되었으면 좋겠다. 다시 말하지만 이럴 땐 클래스 Object의 메소드 equals를 overiding하면 된다. 

equals 를 통해 재정의를 하면 동등한 데이터를 갖게 할 수 있다.

 

그런데 eqauls를 제대로 사용하기 위해서는 hashCode라는 클래스도 함께 구현해야 한다. 하지만 이에 대한 이야기는 우리 수업의 범위를 넘어서고 그 효용(사용 빈도)도 높지 않기 때문에 더 이상 설명을 하지 않겠다. 하지만 이 메소드의 취지를 이해하는 것은 또한 중요하기 때문에 언급을 하지 않을 수는 없었다. 메소드 equals에 대해서 필자가 권고하는 입장은 아래와 같다.

 

1. 객체 간에 동일성을 비교하고 싶을 때는 ==를 사용하지 말고 equals를 이용하자.

2. equals를 직접 구현해야 한다면 hashCode도 함께 구현해야 함을 알고 이에 대한 분명한 학습을 한 후에 구현하자.

3. equals를 직접 구현해야 한다면 eclipse와 같은 개발도구들은 equals와 hashCode를 자동으로 생성해주는 기능을 가지고 있다. 이 기능을 이용하는 것을 고려해보자. 아래 그림을 참고하자.

 4. 그 이유가 분명하지 않다면 비교 연산자 == 은 원시 데이터형을 비교할 때만 사용하자.

 

원시 데이터 형(Primitive Data Type)이란 자바에서 기본적으로 제공하는 데이터 타입으로 byte, short, int, long, float, double, boolean, char가 있다. 이러한 데이터 타입들은 new 연산자를 이용해서 생성하지 않아도 사용될 수 있다는 특징이 있다.

 


 hashcode 

 

 

'📌 java > java' 카테고리의 다른 글

java - Inner Class  (0) 2020.05.13
java - getClass()  (0) 2020.05.01
java - throw new  (0) 2020.04.29
java - throw & throws  (0) 2020.04.28
java - 예외의 강제  (0) 2020.04.28
복사했습니다!