제네릭(Generic)
절묘하게 맞춰진 미완성 그림이다. 제네릭을 활용하면 박싱/언박싱 등을 고려하지 않고, 그래도 갖다쓸 수 있다. 타입에 따라 클래스를 여러 번 만들거나 반복적인 수동 형변환을 해주지 않아도 되서 편리한 듯 하다. 자바는 배우면 배울수록 매력있는 언어인 듯 하다. (자바똥)
public class Main {
public static void main(String[] args) {
제너릭저장소<Integer> a저장소 = new 제너릭저장소<>();
a저장소.setData(500);
System.out.println(a저장소.getData());
}
}
class 제너릭저장소<T>{
private Object data;
public T getData() {
return (T)this.data;
}
public void setData(T inputData) {
this.data = inputData;
}
}
String, Character 그리고 equals()
s3 = "하하";
s4 = "하하"; // 모두 같은 "하하"를 가리킴
s3 += "하"; // s3는 다른 "하하하"를 가리키고, s4 는 "하하"를 가리킴
s1 += "하"; 를 한다면 재활용모드를 작동해서 기존의 "하"는 그대로 있고, s1과 s2가 같!은! "하하"를 가리킬 순 없는가?
s1 = "하";
s2 = "하하"; // 현재 s1는 "하", s2는 "하하"를 가리킴
잘 모르겠다... 좀 더 공부해보고 알아봐야 할 내용이다.
public class Main {
public static void main(String[] args) {
String s1 = "하";
s1 += "하";
String s2 = "하하";
System.out.println(s1 == s2);
System.out.println(s1.equals(s2)); // 객체가 가리키는 것의 동등성을 비교하는 것 (값이 같은지가 아님)
String s3 = "하하";
String s4 = "하하";
System.out.println(s3==s4);
System.out.println(s3.equals(s4));
}
}
확실히 이해가 안되서 찾아보았다. hashcode() 메서드와 equals() 메서드가 관계가 있었다.
(출처 : [기초부터자바] hashcode란? hashcode와 equals의 관계(1) : 네이버 블로그 (naver.com))
Object 클래스의 hashcode() 메서드
기본적으로 Object 클래스 내부에 있는 hashcode() 메서드는 "객체의 해시 코드 값을 반환하고, HashMap에서 제공하는 것과 같은 해시 테이블의 이점을 위해 지원된다" 고 한다. 다시 말해, O(n)의 접근을 통해 값에 접근하는 방식이 아닌 key-value를 통해 O(1)에 접근하기 위해 사용된다. 당연히 다른 장점도 있을 것이지만 알고리즘 공부를 하면서 hashMap을 그러한 용도로 사용해왔다.
다시 돌아가서 객체들은 서로 다른 주소 값을 가진다. 아래의 코드를 돌려보면 각 객체마다 서로 다른 주소 값을 가지는 것을 확인할 수 있다. hashcode 메서드는 Object 클래스에서 정의된 그대로의 메서드이다. (값 자체가 주소값과 연관이 있는 상태) 또한 사람 클래스도 hashcode메서드를 오버라이드하지 않고 초기화했기 때문에 Object클래스에서 정의된 그대로의 메서드이다.(모든 클래스는 Object로부터 상속 받기 때문에) 하지만 String의 경우 hashcode가 주소값과 전혀 관련이 없다고 한다. String 클래스의 hashcode는 오버라이드 과정에서 새롭게 정의된다고 한다. 정말 중요한 내용이였다.
package Study.JavaEquals01;
public class Main{
public static void main(String[] args){
Object obj = new Object();
System.out.println("Object의 출력값 : " + obj);
System.out.println("Object의 hashcode 출력값 : " + obj.hashCode());
System.out.println();
사람 a사람 = new 사람();
System.out.println("사람 클래스의 출력값 : " + a사람);
System.out.println("사람 클래스의 hashcode 출력값 : " + a사람.hashCode());
String s = "하하";
System.out.println("s의 hashcode 출력겂 : " + s.hashCode());
}
}
class 사람 { }
자 여기서 hashcode의 의미를 생각해보자. 예를 들어, hashMap을 사용할 때, value는 여러 개가 존재할 수 있지만, key가 여러 개가 존재할 수 있는가? 절대 그럴 수 없다는 것을 알 것이다. 유일한 key를 통해 value에 접근하는 것이 장점인 것 이다. 다시 돌아와서 왜 String 클래스는 hashcode를 새롭게 정의할까? 눈치를 챘을 수도 있다. (나도 이 부분에서 느낌이 확 왔다.) 유일한 key를 통해 value에 접근하는 hashcode의 특성상 같은 값에 대해서는 유일한 주소값을 가져야한다. 그렇지 않으면 hashcode가 존재하는 의미가 없어지는 것이다.
그러므로 인위적으로 String 클래스 내부에서 hashcode 메소드를 재정의하여 같은 String 객체에 대해선 같은 hashcode를 리턴해주도록 만들어주는 것이다. (점점 궁금한 점이 풀리고 있다.) 그리고 깊게 들어가서 equals()를 재정의하여 사용하는 방법 등이 존재하지만 더 자세히 설명해주시는 분이 있다.(이분의 글을 참고하여 이 부분을 작성하였다.) 궁금하다면 참고하길 바란다. (더 깊게 공부하고 싶지만 천천히 배워야할 것 같다. 아직 자바 7일차이기 때문에 욕심내지 말자.)
출처 : https://blog.naver.com/travelmaps/220931531769
같은 값을 가진 객체를 equals()를 해보면 false가 나온다. 위에서 설명했듯이, Object 클래스에서 상속받은 hashcode는 오버라이드되지 않았기 때문에 사용자가 인위적으로 재정의해줘야 한다. (즉, 두 객체가 같은 객체임을 의미하는 equals()를 오버라이드해야 한다.) 필요하다면 hashcode() 메서드도 재정의해야 한다. 정리하자면,
equals() 메서드에 true가 나오는 두 객체의 hashcode는 같아야한다. 그래야 hashcode가 의미가 있는 것이다.
(https://blog.naver.com/travelmaps/220931531769 의 글을 보고 많이 배웠다... 감사합니다.)
public class Main {
public static void main(String[] args) {
사람 a사람1 = new 사람("홍길동",22);
사람 a사람2 = new 사람("홍길동",22);
// 동치성
if(a사람1 == a사람2) {
System.out.println("1 yes");
}
// 동등성(둘의 스펙이 같냐?)
if(a사람1.equals(a사람2)) {
System.out.println("2 yes");
}
}
}
class 사람 {
String 이름;
int 나이;
사람(String 이름, int 나이) {
this.이름 = 이름;
this.나이 = 나이;
}
public boolean equals(Object o) {
if (o instanceof 사람 == false) return false;
사람 other = (사람)o;
if(this.이름.equals(other.이름) == false) return false;
if(this.나이 != other.나이) return false;
return true;
}
}