- 객체 비교
- 1_equals 재정의12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
import
java.util.Objects;
// 1. Object.equals()를 재정의하지 않을 경우
// 모든 객체는 오직 자기 자신과 동일하다.
// 2. 객체 동일성이 아닌 논리적 동일성의 개념을 제공하기 위해서는
// equals()를 재정의해야 한다.
class
Point {
private
int
mX;
private
int
mY;
public
Point(
int
x,
int
y) {
this
.mX = x;
this
.mY = y;
}
@Override
public
int
hashCode() {
final
int
prime =
31
;
int
result =
1
;
result = prime * result + mX;
result = prime * result + mY;
return
result;
}
@Override
public
boolean
equals(Object obj) {
// 1. 자기 자신인지 검사 - 성능
if
(
this
== obj)
return
true
;
// 2. null 인지 체크 (모든 객체는 null과 동치 관계가 있지 않다.)
if
(obj ==
null
)
return
false
;
// 3. 인자의 자료형이 정확한지 검사.
if
(!(obj
instanceof
Point))
return
false
;
// 4. 자료형 변환
Point p = (Point) obj;
// 5. 중요 필드 점검
return
mX == p.mX && mY == p.mY;
}
}
class
Unit {
private
Point position;
private
Point start;
// 1. 객체에 대한 참조는 null이 될 수 있다.
// 필드가 많아지면 Objects.equal을 고려하자.(Google Guava / 1.7)
@Override
public
boolean
equals(Object obj) {
if
(obj ==
this
)
return
true
;
if
(obj ==
null
)
return
false
;
if
(!(obj
instanceof
Unit))
return
false
;
Unit p = (Unit) obj;
// if (position == null) {
// if (p.position != null) return false;
// } else if (!position.equals(p.position)) return false;
// return true;
return
Objects.equals(position, p.position) && Objects.equals(start, p.start);
}
}
public
class
Example5 {
public
static
void
main(String[] args) {
Point p1 =
new
Point(
10
,
20
);
Point p2 =
new
Point(
10
,
20
);
if
(p1.equals(p2)) {
System.out.println(
"Same"
);
}
else
{
System.out.println(
"Not Same"
);
}
}
}
- 2_BigDecimal, 배열 비교
: 컴퓨터는 태생적으로 부동소수점을 정확히 표현할 수 없다.(2진수로 표현하기 때문에...)
: float, double은 == 로 비교하면 안된다. -> BigDecimal 사용
: 배열의 내용 비교 -> Arrays.equals()1234567891011121314151617181920212223242526272829303132333435363738package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
import
java.math.BigDecimal;
import
java.util.Arrays;
// 1. float이나 double은 ==으로 비교하면 안된다.
// 2. 정밀한 연산을 필요로 한다면 BigDecimal 을 사용해야 한다.
public
class
Example6 {
public
static
void
main(String[] args) {
double
value1 =
2.0
-
1.1
;
double
value2 =
0.9
;
// if (value1 == value2) {
if
(Math.abs(value1 - value2) <
0.001
) {
System.out.println(
"same"
);
}
else
{
System.out.println(
"not same"
);
}
BigDecimal v =
new
BigDecimal(
2.0
).subtract(
new
BigDecimal(
1.1
));
System.out.println(v);;
// 주의 사항 : BigDecimal(String)의 생성자를 사용해야 한다.
BigDecimal v2 =
new
BigDecimal(
"2.0"
).subtract(
new
BigDecimal(
"1.1"
));
System.out.println(v2);;
//-------------------------------------------------
// 3. 배열 내용을 비교하려면 Object.equals() 가 아닌 Arrays.equals()
// 사용해야 한다.
int
[] arr1 =
new
int
[
20
];
int
[] arr2 =
new
int
[
20
];
System.out.println(arr1.equals(arr2));
System.out.println(Arrays.equals(arr1, arr2));
}
}
객체 복제
: 인자 전달 방식
-> Call-by-Value vs. Call-by-Reference
: 객체 복사 방식
-> 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy)123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869class
Num {
public
int
num;
public
Num(
int
num) {
this
.num = num;
}
}
public
class
CallBy {
// 값을 복사해서 인자 전달
// value는 객체에 대한 레퍼런스 값, 프리미티브 타입의 값
public
void
callByValue(
int
a,
int
b) {
System.out.println(
"callByValue in method: "
+ a +
" "
+ b);
int
swap = a;
a = b;
b = swap;
System.out.println(
"callByValue out method: "
+ a +
" "
+ b);
}
// 해당 객체를 참조하는 객체를 복사해서 인자 전달(얕은 복사)
// 해당 객체의 주소값을 직접 넘기는 것이 아니라 객체를 가리키고 있는 또 다른 주소값을 만들어서 넘긴다
void
callByReference(Num a, Num b) {
System.out.println(
"callByReference in method: "
+ a.num +
" "
+ b.num);
Num swap = a;
a = b;
b = swap;
System.out.println(
"callByReference out method: "
+ a.num +
" "
+ b.num);
}
// 객체의 멤버 필드값에 대한 복사가 필요(깊은 복사)
void
callByReference2(Num a, Num b) {
System.out.println(
"callByReference2 in method: "
+ a.num +
" "
+ b.num);
int
swap = a.num;
a.num = b.num;
b.num = swap;
System.out.println(
"callByReference2 out method: "
+ a.num +
" "
+ b.num);
}
public
static
void
main(String[] args) {
CallBy call =
new
CallBy();
int
a =
5
;
int
b =
10
;
Num n1 =
new
Num(
5
);
Num n2 =
new
Num(
10
);
call.callByValue(a, b);
System.out.println(
"callByValue main method: "
+ a +
" "
+ b +
"\n"
);
//callByValue in method: 5 10
//callByValue out method: 10 5
//callByValue main method: 5 10
call.callByReference(n1, n2);
System.out.println(
"callByReference main method: "
+ n1.num +
" "
+ n2.num +
"\n"
);
//callByReference in method: 5 10
//callByReference out method: 10 5
//callByReference main method: 5 10
call.callByReference2(n1, n2);
System.out.println(
"callByReference2 main method: "
+ n1.num +
" "
+ n2.num);
//callByReference2 in method: 5 10
//callByReference2 out method: 10 5
//callByReference2 main method: 10 5
}
}
- 1_clone, Cloneable
: 객체에 대한 깊은 복사
: 상속을 해주기 위한 클래스를 설계할 때, 잘 동작하는 protected clone 메소드를 그 클래스에 두지 않는다면 서브 클래스에서 Cloneable 인터페이스를 제대로 구현할 수 없다.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
import
com.google.common.base.MoreObjects;
class
Point
implements
Cloneable {
private
int
x;
private
int
y;
public
Point(
int
x,
int
y) {
this
.x = x;
this
.y = y;
}
public
int
getX() {
return
x;
}
public
void
setX(
int
x) {
this
.x = x;
}
public
int
getY() {
return
y;
}
public
void
setY(
int
y) {
this
.y = y;
}
@Override
public
String toString() {
return
"("
+ x +
", "
+ y +
")"
;
}
@Override
public
Point clone() {
try
{
return
(Point)
super
.clone();
}
catch
(CloneNotSupportedException e) {
e.printStackTrace();
}
return
null
;
}
}
// 객체 복제 하기
// 1. clone() 함수를 오버라이드 한다. (protected -> public)
// : 오버라이딩할 메소드는 부모의 접근 제한자와 같거나 접근하기 더 쉬워야 한다.
// 2. Cloneable 인터페이스를 구현해야 한다.
// : 어떤 객체가 복제를 허용한다는 사실을 알리는데 쓰이는 용도이다.
// 객체가 Cloneable 인터페이스를 구현하고 있으면, Object.clone() 은
// 객체가 가지고 있는 모든 멤버를 복사한다.
class
Unit
implements
Cloneable {
private
String name;
private
int
age;
private
Point position;
// 중요 : 변경 가능 객체에 대한 참조를 가지고 있으면 문제가 발생한다.
public
Unit(String name,
int
age, Point pos) {
this
.name = name;
this
.age = age;
this
.position = pos;
}
public
void
setPos(
int
x,
int
y) {
position.setX(x);
position.setY(y);
}
// public Object clone() {
// 공변 반환형 : 재정의 메소드의 리턴 타입은 재정의 되는 메소드의
// 리턴 타입의 하위 클래스가 될 수 있다.(1.5)
// @Override
// public Unit clone() {
// try {
// return (Unit) super.clone();
// } catch (CloneNotSupportedException e) {
// e.printStackTrace();
// }
//
// return null;
// }
@Override
public
Unit clone() {
try
{
// 1. 전체 복사 후
Unit result = (Unit)
super
.clone();
// 2. 변경 가능 객체 복제
result.position = position.clone();
return
result;
}
catch
(CloneNotSupportedException e) {
e.printStackTrace();
}
return
null
;
}
@Override
public
String toString() {
return
MoreObjects.toStringHelper(
this
).add(
"name"
, name).add(
"age"
, age)
.add(
"pos"
, position).toString();
}
}
public
class
Example8 {
public
static
void
main(String[] args) {
Unit p1 =
new
Unit(
"Tom"
,
42
,
new
Point(
0
,
0
));
// Unit p2 = (Unit) p.clone();
Unit p2 = p1.clone();
p2.setPos(
10
,
20
);
// !!!
System.out.println(p1);
System.out.println(p2);
}
}
- 2_생성자 방어 복사
: 접근자를 이용하여 클라이언트가 값을 변경할 수 있다. -> 캡슐화가 깨짐
: 객체를 복사할 때 객체의 참조를 리턴하는 것이 아니라 객체의 복사본을 리턴한다. pos.setX(9999)로 x값을 변경해도 복사한 객체의 값을 바꾸므로 기존의 객체의 값은 그대로 유지된다.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
import
com.google.common.base.MoreObjects;
public
class
Example9 {
public
static
void
main(String[] args) {
// 1. 생성자 방어 복사가 필요하다.
// : 인자의 유효성을 검사하기 전에 복사하고 나서,
// 원본이 아닌 복사본의 유효성을 검사해야 한다.
Point pos =
new
Point(
100
,
200
);
// Unit unit = new Unit(pos);
String name =
"Tom"
;
Unit unit =
new
Unit(pos, name);
pos.setX(
9999
);
// 2. 접근자 메소드에서도 참조를 방어 복사하여 리턴해야 한다.
pos = unit.position();
pos.setX(
9999
);
System.out.println(unit);
}
}
class
Unit {
private
Point position;
private
String name;
public
Unit(Point pos, String name) {
position = pos.clone();
// position = pos;
this
.name = name;
}
public
String name() {
return
name;
}
public
Point position() {
return
position.clone();
}
@Override
public
String toString() {
return
MoreObjects.toStringHelper(
this
)
.add(
"pos"
, position).add(
"name"
, name).toString();
}
}
class
Point
implements
Cloneable {
private
int
x;
private
int
y;
public
Point(
int
x,
int
y) {
this
.x = x;
this
.y = y;
}
public
int
getX() {
return
x;
}
public
void
setX(
int
x) {
this
.x = x;
}
public
int
getY() {
return
y;
}
public
void
setY(
int
y) {
this
.y = y;
}
@Override
public
String toString() {
return
"("
+ x +
", "
+ y +
")"
;
}
@Override
public
Point clone() {
try
{
return
(Point)
super
.clone();
}
catch
(CloneNotSupportedException e) {
e.printStackTrace();
}
return
null
;
}
}
- 3_hashCode
: Google Guava를 사용하여 toString 재정의
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | package kr.co.ioacademy; //iocademy 윤찬식 강사님 import java.util.HashMap; import java.util.Map; import java.util.Objects; import com.google.common.base.MoreObjects; // 핵심 : 같은 객체는 동일한 해시 코드 값을 가져야 한다. // 즉 equals를 재정의한 클래스는 반드시 hashCode도 재정의해야 한다. // 그래야 HashMap, HashSet, HashTable 등 hash 기반 컬렉션과 함께 사용하면 오동작 하지 않는다. public class Example7 { public static void main(String[] args) { Map<Person, String> m = new HashMap<>(); m.put( new Person( "Tom" , 42 ), "Google" ); System.out.println(m.get( new Person( "Tom" , 42 ))); // toString()을 잘 만들어 놓으면 편리하다. System.out.println( new Person( "IU" , 42 )); } } class Person { private String name; private int age; public Person(String name, int age) { this .name = name; this .age = age; } @Override public String toString() { return MoreObjects.toStringHelper( this ).add( "name" , name).add( "age" , age).toString(); } @Override public int hashCode() { // equals 에 이용된 중요 필드를 이용해서 hash를 생성하면 된다. return Objects.hash(name, age); } @Override public boolean equals(Object o) { if (o == this ) return true ; if (o instanceof Person) { Person p = (Person) o; return Objects.equals(name, p.name) && age == p.age; } return false ; } } |
'Programing > Java' 카테고리의 다른 글
Effective Java - 스레드(1) (0) | 2016.03.07 |
---|---|
Effective Java - 불변 객체 (0) | 2016.03.07 |
Effective Java - 객체 소멸 (0) | 2016.03.04 |
Junit 사용하기 (0) | 2016.03.03 |
Effective Java - 객체 생성 (0) | 2016.03.03 |