언어 자료구조 알고리즘/디딤돌 Java 언어 Part1

[Java] 5. 6 모든 클래스의 super 클래스인 Object

언제나휴일 2016. 11. 14. 22:11
반응형

출간일 2016년 11월 28일

판매가 2000원

형태 ebook


이 책의 모든 내용은 http://ehpub.co.kr에 공개하고 있습니다.

학습에 도움이 되시면 ebook을 구입하여 소장하시면 감사하겠습니다.

언제나 휴일 출판사의 수익금의 대부분은 아프리카에 기부하고 있습니다.



 5. 6 모든 클래스의 super 클래스인 Object

 

 Java 언어에서의 모든 형식은 묵시적으로 Object 클래스에서 파생한 형식입니다. 이는 모든 형식의 계층 구조에서 Object 형식이 root라는 말이기도 합니다. 여기에서 묵시적이라는 말은 특정 형식을 정의할 때 Object 형식을 기반으로 파생하는 것을 표시하지 않지만 내부적으로는 Object의 파생 형식이라는 말입니다.

 

 따라서 다형성의 주요 특징인 기반 형식 변수로 파생 형식 개체를 참조할 수 있다는 특징에 따라 Object 형식 변수에는 모든 형식 개체를 대입하여 사용할 수 있습니다.

 

 Object 클래스에는 디폴트 기본 생성자와 equals, hashCode, toString 등의 메서드를 제공하고 있으며 개발자가 정의하는 형식에서 필요에 따라 재정의하여 사용할 수 있습니다.

 

 그리고 Object의 멤버는 아니지만 Cloneable 인터페이스를 기반으로 정의하는 구현 클래스에서는 자신을 복제한 개체를 반환하는 Object clone() 메서드를 제공하고 있습니다.


5.6.1 equals 메서드

 

 euuals 메서드는 두 개의 개체가 같은지 판별하는 것이 디폴트입니다. 대신 값 형식과 일부 클래스(대표적으로 String 클래스)에서는 내부의 값이 같은지 판별합니다.

 

 다음의 예제 코드는 equals 메서드를 재정의할 때와 하지 않을 때를 비교하기 위한 예제입니다. MyData 클래스에서는 equals 메서드를 재정의하였고 MyInt 클래스에서는 재정의하지 않았습니다.

 

 그리고 테스트 코드에서는 같은 값을 갖는 MyData 개체를 두 개 생성한 후에 equals 메서드로 비교하고 == 연산자로 비교합니다. 클래스 형식의 equals 메서드 디폴트 동작은 같은 개체를 참조하는지를 비교하는 것이기 때문에 결과는 false입니다. 물론 == 연산의 결과도 마찬가지입니다.

MyData md1 = new MyData(1);

MyData md2 = new MyData(1);

System.out.println(md1.equals(md2));

System.out.println(md1==md2);

 

 하지만 같은 값을 갖는 MyInt 개체를 생성한 후에 equals 메서드 호출하면 개발자가 정한 코드가 동작하기 때문에 개발자 의도에 맞게 내부 값만 같아도 true값을 반환하게 정의할 있습니다.

public class MyInt {

     int value;

     public MyInt(int value){

               this.value = value;

        }

        public boolean equals(Object obj){

               if(obj instanceof MyInt){

                       MyInt mi = (MyInt)obj;

                       return value == mi.value;

               }

               return false;

        }

}

 

 이처럼 정의하면 같은 값을 갖는 개의 MyInt 개체를 equals 메서드로 비교하면 true 값을 반환합니다. 물론 때도 == 연산의 결과는 false입니다.

MyInt mi1 = new MyInt(1);

MyInt mi2 = new MyInt(1);

System.out.println(mi1.equals(mi2));

System.out.println(mi1==mi2);

 

 

//equals 메서드를 재정의하지 않은 MyData 클래스

public class MyData {

        int num;

        public MyData(int num){

               this.num = num;

        }

}

//equals 메서드를 재정의한 MyInt 클래스

public class MyInt {

        int value;

        public MyInt(int value){

               this.value = value;

        }

        public boolean equals(Object obj){

               if(obj instanceof MyInt){

                       MyInt mi = (MyInt)obj;

                       return value == mi.value;

               }

               return false;

        }

}

//equals 메서드 재정의

public class Program {

        public static void main(String[] args){

               MyData md1 = new MyData(1);

               MyData md2 = new MyData(1);

               System.out.println(md1.equals(md2));

               System.out.println(md1==md2);

               MyInt mi1 = new MyInt(1);

               MyInt mi2 = new MyInt(1);

               System.out.println(mi1.equals(mi2));

               System.out.println(mi1==mi2);

        }

}

false

false

true

false

[소스 5.13] equals 메서드 재정의 예

 

5.6.2 hashCode 메서드

 

 Object 클래스의 hashCode 메서드에서는 Java 플랫폼에서 특정 개체를 빠르게 검색할 수 있게 해쉬 테이블에 저장하는 것과 연관이 있습니다. hashCode는 해당 개체를 해쉬 테이블에 저장하였을 때 빨리 찾기 위한 해쉬 코드값을 반환하는 것입니다. 실제 프로그램 실행에서 서로 다른 개체가 같은 해쉬 코드값을 갖을 확률은 지극히 낮기 때문에 개체를 판별하는데 사용하는 이도 있지만 이는 완벽한 신뢰성을 보장한다고 볼 수는 없습니다. 확률적으로 같은 값을 반환하는 것이 낮은 것일 뿐 절대적인 것은 아니기 때문입니다.

 

 물론 개발자는 이 hashCode 메서드를 재정의할 수 있습니다. 다음 예제에서는 정적 멤버로 seq를 선언하고 개체를 생성할 때마다 1씩 증가한 후에 개체의 멤버 hv 에 대입하였습니다. 그리고 hashCode 메서드를 재정의하여 유일한 값으로 사용할 수 있게 하였습니다.

 

 그렇지만 이러한 방법을 추전하는 것은 아니며 이렇게 할 수도 있다는 것을 보여주는 것일 뿐입니다.

 

//hashCode 메서드를 재정의

public class MyInt {

        static int seq=0;

        int value;

        int hv;

        public MyInt(int value){

               seq++;

               hv = seq;

               this.value = value;

        }      

        public int hashCode(){

               return hv;

        }

}

//hashCode 메서드 재정의

public class Program {

        public static void main(String[] args){

               MyInt mi1 = new MyInt(1);

               MyInt mi2 = new MyInt(1);

               System.out.println(mi1.hashCode());

               System.out.println(mi2.hashCode());

        }

}

1

2

[소스 5.14] hashCode 메서드 재정의 예

 

5.6.3 toString 메서드

 

 Object 클래스에서는 toString 메서드를 제공하여 해당 클래스 혹은 개체를 대표하는 문자열을 반환할 수 있게 하고 있습니다.

 

 기본 값 형식들은 정적 클래스의 toString 정적 메서드를 통해 해당 값을 문자열로 변환할 수 있게 제공하고 있습니다.

int i=3;

System.out.println(Integer.toString(i));

 

 그리고 String 형식은 내용을 출력하게 정의하고 있습니다. 예를 들어 문자열 http://ehpub.co.kr 대입하면 toString 메서드의 반환값도 http://ehpub.co.kr입니다.

String s="http://ehpub.co.kr";

System.out.println(s.toString());

 

 만약 개발자가 정의한 형식에 toString 메서드를 재정의하지 않으면 디폴트 문자열 값은 클래스 이름@해쉬 코드값입니다.

 

 다음 예제 코드는 toString 메서드를 재정의하지 않은 Def_toString 클래스와 재정의한 My_toString 클래스를 정의하고 개체를 생성하여 확인하는 코드입니다. 더불어 int 형식과 String 형식도 테스트하고 있습니다.

 

//toString 메서드를 재정의하지 않은 클래스

public class Def_toString {

        int value;

        public Def_toString(int value){

               this.value = value;

        }

}

//toString 메서드를 재정의한

public class My_toString {

        int value;

        public My_toString(int value){

               this.value = value;

        }

        public String toString(){   

               return Integer.toString(value);

        }

}

//toString 메서드 재정의

public class Program {

        public static void main(String[] args){

               Def_toString dts = new Def_toString(1);            

              

               System.out.println(dts.toString());

               System.out.println(dts.hashCode());

              

               My_toString mts = new My_toString(1);              

              

               System.out.println(mts.toString());

              

               int i=3;             

               System.out.println(Integer.toString(i));

              

               String s="http://ehpub.co.kr";

               System.out.println(s.toString());

        }

}

Def_toString@15db9742

366712642

1

3

http://ehpub.co.kr

[소스 5.15] toString 메서드 재정의 예

 

5.6.4 Cloneable 인터페스를 구현한 클래스의 clone 메서드

 

 Java에서는 개체 자신을 복제하는 메서드를 제공할 때 Cloneable 인터페이스를 기반으로 구현 클래스를 정의하는 것을 권고하고 있습니다.

 

 다음 예제는 유일한 번호를 멤버로 갖고 변경 가능한 이름을 멤버로 갖는 Student 클래스를 Cloneable 인터페이스를 기반으로 구현 클래스 형태로 정의한 예제입니다.

 

 코드를 살펴보면 정적 멤버로 가장 최근에 부여한 학생 번호를 기억하는 last_snum을 캡슐화하였고 학생의 번호 snum final 키워드를 명시하여 값을 변경하지 못하게 캡슐화하였습니다. 그리고 변경 가능한 이름은 name 이름으로 캡슐화하였습니다.

public class Student implements Cloneable {

     final int snum;

     static int last_snum=0;

     String name;

 

 생성자에서는 이름을 입력인자로 받으며 last_snum 1 증가한 후에 snum에 설정하여 생성합니다. 물론 이름은 인자로 전달받은 값으로 설정합니다.

     public Student(String name){

             last_snum++;

             snum = last_snum;

             this.name = name;

     }

 

 그리고 clone 메서드에서는 자신의 이름을 입력 인자로 학생 개체를 복제한 후에 반환합니다.

     public Object clone(){

             Student cs = new Student(name);

             return cs;

     }

 

 이처럼 구현하면 학생 번호는 생성하는 순서에 맞게 배정하면서 이름은 같은 개체를 생성할 수 있습니다.

 

//멤버의 일부만 복재하는 clone 메서드 재정의한 클래스

public class Student implements Cloneable {

        final int snum;

        static int last_snum=0;

        String name;

        public Student(String name){

               last_snum++;

               snum = last_snum;

               this.name = name;

        }

        public String toString(){

               return Integer.toString(snum)+","+name;

        }

        public Object clone(){

               Student cs = new Student(name);

               return cs;

        }

}

//부분적인 멤버를 복재하는 clone 메서드

public class Program {

        public static void main(String[] args){

               Student os = new Student("홍길동");

               Student cs = (Student)os.clone();

               System.out.println(os);

               System.out.println(cs);

        }

}

1,홍길동

2,홍길동

[소스 5.16] 부분적인 멤버를 복재하는 clone 메서드 예


반응형