Java Objects 클래스의 메소드들 까봤습니다!

자바 Objects 클래스에 대해서

- java 1.7버전에서 등장한 클래스 

- java.util 패키지 

- 객체에서 작업하기위한 여러가지 정적 유틸리티 클래스로 구성된 클래스

- 기능들에는 객체의 hashcode를 계산하기 위해 Null 안전 혹은 Null 허용 메소드들을 포함하고 있으며 객체의 문자열을 반환하고 두 객체를 비교합니다.

- 아래 내용은 https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html 페이지를 참조하여 포스팅하였습니다.

 

 

 

<T> int compare(T a, T b, Comparator<? super T> c)

public static <T> int compare(T a, T b, Comparator<? super T> c) {
    return (a == b) ? 0 :  c.compare(a, b);
}

- a, b 인자가 동일하면(같은 값이면) 0을 반환하고 아닌 경우 c.compare(a, b)의 결과를 반환합니다.

- c.compare 메소드는 직접 정의.

Ex)

int k = Objects.compare(1, 2, new Comparator<Integer>() {
    @Override
    public int compare(Integer a, Integer b) {
        return a - b; //리턴문은 알아서..
    }
});

if문의 조건으로 변수k의 변화되는 값을 사용해서 처리하면 될 듯하다. (음수면 a가 작은거, 양수면 a가 큰거 이렇게..)

위의 Comparator 익명객체는 람다식으로 처리할 수도 있다.

int k = Objects.compare(1, 2, (a, b) -> a - b);

코드의 줄 수를 절약할 수 있다.

 

 

 

 

 

boolean deepEqauls(Object a, Object b)

- 인자들이 deeply 하게 같으면 true 아니면 false를 반환.

- 두 인자가 모두 null 인경우 deeply 동일.

- 두 인자가 모두 배열형태라면 Arrays.deepEquals() 메소드를 타게됨.(배열의 요소를 순차비교)

- 단순 참조타입은 Object 클래스의 equals 메소드를 통해 비교됨.

 

- 참조하여 같이보면 좋을 글

2019/12/17 - [프로그래밍 언어/[Java] Study 내용 ] - Objects 클래스의 equals와 deepEquals 메소드 차이.

 

Objects 클래스의 equals와 deepEquals 메소드 차이.

Objects 클래스의 equals와 deepEquals 메소드 차이. deepEquals 메소드 public static boolean deepEquals(Object a, Object b) { if (a == b) return true; else if (a == null || b == null) return false; els..

sas-study.tistory.com

 

 

 

 

 

int hashCode(Object o)

- null이 아닌 인자의 해쉬코드를 반환하고 null인 경우는 0을 리턴하는 메소드

- 해쉬코드란 개별 오브젝트에 대해 특정 주소값이 있다면 이를 정수로 변환해주는 메소드이다.

- 해당 메소드는 native 키워드가 붙어있으며 java로 구현되어 있지 않다.

int hash(Object… values)

public static int hash(Object... values) {
    return Arrays.hashCode(values);
}

ex) 

int k = Objects.hash(tt,1, 3, “d”);

- values라는 일련의 파라미터들의 해쉬코드를 생성합니다.

- values 메소드는 Arrays.hashCode(Object[]) 메소드의 인자로 들어가 실행하게 되는데, 이때 배열의 요소들을 이용하여 해쉬코드를 생성해냅니다.

- 하지만 values에 들어가는 인자가 1개인 경우의 해쉬코드는 hashCode() 메소드로 생성한 값과 다를 수 있습니다. 이 경우는 hashCode() 메소드를 이용한 해쉬코드값이 정확한 값입니다.

 

 

 

 

 

String toString(Object o) , String toString(Object o, String nullDefault)

public static String toString(Object o) {
    return String.valueOf(o);
}

public static String toString(Object o, String nullDefault) {
    return (o != null) ? o.toString() : nullDefault;
}

- null인 경우 디폴트 값으로 내놓을 값을 설정하거나 설정하지 않는 모드 2가지가 있음.

- 이는 오버로딩 형태임

- null이 아닌 인자에 대해서 toString() 메소드를 실행한 결과를 리턴합니다.(String.valueOf 메소드도 결국 String의 toString() 메소드를 호출하는 것과 같음.(null이 아닌 경우)

- 아래의 경우는 그냥 nullDefault로 들어온 인자를 반환함.

 

 

 

 

 

<T> T requireNonNull(T obj)

public static <T> T requireNonNull(T obj) {    
    if (obj == null)        
        throw new NullPointerException();    
    return obj;
}

- 주로 파라미터의 validation을 잡기위해서 설계된 메소드.

- 오버로딩된 형태

- 인자로 들어온 파라미터가 null이 아닌경우 다시 그대로 반환해주고 null인 경우 NullPointerException 예외를 발생시킨다.

- 예를 들어

public Foo(Bar bar) {
     this.bar = Objects.requireNonNull(bar);
 }

<T> T requireNonNull(T obj, String message),

- 두번째 인자가 같이들어오는 경우, 해당 메시지를 출력하는 커스터마이징 NullPointerException 예외를 발생시켜준다.

public static <T> T requireNonNull(T obj, String message) {
    if (obj == null)
        throw new NullPointerException(message);
    return obj;
}

- 예를 들어

public Foo(Bar bar, Baz baz) {
     this.bar = Objects.requireNonNull(bar, "bar must not be null");
     this.baz = Objects.requireNonNull(baz, "baz must not be null");
 }

<T> T requireNonNull(T obj, Supplier<String> messageSupplier)

- 세번째 오버로딩 메소드는 Supplier 객체를 받습니다. 메세지를 리턴하는 supplier를 매개변수로 전달합니다. 

public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
    if (obj == null)
        throw new NullPointerException(messageSupplier.get());
    return obj;
}

- 이는 그냥 문자열을 처리할 때와는 약간 다르게 처리됩니다.

예를들어, 다음 소스에서

Objects.requireNonNull(obj, generateString(someParam)); //2번째 메소드인 경우임.

generateString() 메소드가 requireNonNull 메소드 내부에서 obj가 null임을 판별하기 전에 실행되게 됩니다. 그런데 만약 null이 아닌경우에는 해당 메시지는 필요없는 로직이 되는 것이죠. Null 인경우는 사용이 되구요.

이러한 비효율적인 로직을 줄이기 위해서 supplier를 lazy하게 실행하게 됩니다.

lazy하다는 것은 null판별을 하고 나서야 message를 정하는 소스가 실행되는 것입니다. 

 

- 코드상으로 보자면 

1. 두번째 String message를 매개변수로 전달하는 경우 직접 문자열을 “Error!!!” 이런식으로 문자열 리터럴을 넣는 경우는 괜찮지만 generateString () 메소드 예제처럼 메소드의 리턴값을 전달하는 경우는 먼저 파라미터에 값을 매핑하기 위해 generateString() 메소드를 실행하고 변수에 매핑할 수 밖에 없는 구조이기 때문에 null 인지 아닌지에 상관없이 무조건 실행되고 변수에 매핑됩니다.

 

2. 하지만 세번째 Supplier같은 경우는 null타입 체크를 먼저한 후 Exception을 호출하게 될 때 비로소 supplier에서 넘어온 문자열을 get() 메소드를 통해 반환해줍니다.

 

만약 에러메시지를 전달하는 로직에 있어서 단순 문자열을 넘기는 경우는 두번째가 좋은 코드가 될테지만 여러 로직이 섞여서 메소드의 로직을 타고 에러메시지를 리턴해주는 경우에는 supplier 형태를 이용하는 것이 효율적인 방법입니다.

 

 

 

 

 

boolean isNull(Object obj)

- input으로 들어온 파라미터가 null 인 경우 true를 반환하고 아니면 false.

- 코드를 더 가시적으로 읽으면서 코딩할 수 있게 해주는 메소드형태임.

boolean nonNull(Object obj)

- isNull과는 반대로 not null 이면 true를 반환하고, null인 경우 false

- 코드를 더 가시적으로 읽으면서 코딩할 수 있게 해주는 메소드형태임.

 

isNull, nonNull 메소드 공통.

- 자바8에서 stream API를 이용하여 filter할 때 Predicate로 사용할 수 있음.

예를 들어.

public static void main(String[] args) {

    List<String> list1 = new ArrayList<>();
    list1.add("하나");
    list1.add(null);
    list1.add("셋");

    List<String> list2 = new ArrayList<>();
    list2.add("하나");
    list2.add("둘");
    list2.add("셋");

    list1.stream().filter(Objects::isNull).forEach(e -> System.out.println(e));
    list2.stream().filter(Objects::nonNull).forEach(e -> System.out.println(e));
}

 

 

 

댓글

Designed by JB FACTORY