본문 바로가기

# 02/Java

[윤성우 열혈자바] 28-2. Optional 클래스

반응형

NullPointerException 예외의 발생 상황 1


class Friend {  // 친구 정보

   String name;

   Company cmp;  // null   있음

 

   public Friend(String n, Company c) {

      name = n;

      cmp = c;

   }

   public String getName() { return name; }

   public Company getCmp() { return cmp; }

}

 

class Company {  // '친구 정보' 속하는 '회사 정보'

   String cName;

   ContInfo cInfo;  // null   있음

   

   public Company(String cn, ContInfo ci) {

      cName = cn;

      cInfo = ci;

   }

   public String getCName() { return cName; }

   public ContInfo getCInfo() { return cInfo; }

}


class ContInfo {  // '회사 정보' 속하는 '회사 연락처'

   String phone;  // null   있음

   String adrs;  // null   있음

 

   public ContInfo(String ph, String ad) {

      phone = ph;

      adrs = ad;

   }

   public String getPhone() { return phone; }

   public String getAdrs() { return adrs; }

}


가장 좋은 것은 null 초기화  인스턴스 변수가 없는 !

그러나 허용해야 하는 경우도 있다.








NullPointerException 예외의 발생 상황 2


public static void showCompAddr(Friend f) {

   String addr = null;

   if(f != null) { // 인자로 전달된 것이 null  수도 있으니

      Company com = f.getCmp();

     

      if(com != null) { // 회사 정보가 없을 수도 있으니

         ContInfo info = com.getCInfo();

     

         if(info != null) // 회사의 연락처 정보가 없을 수도 있으니

            addr = info.getAdrs();

      }

   }

   

   if(addr != null) // 위의 코드에서 주소 정보를 얻지 못했을  있으니

      System.out.println(addr);

   else

      System.out.println("There's no address information.");

}


null 가능성에 대비하는 코드의 작성은 번거롭고

 코드 스타일도 만족스러운 편은 아니다.

 

이를 해결하기 위해 등장한 것이 Optional 클래스








Optional 클래스의 기본적인 사용 방법 1


public final class Optional<T> extends Object {

   private final T value;  //  참조변수를 통해 저장을 하는 일종의 래퍼 클래스

   ....

}


public static void main(String[] args) {

   Optional<String> os1 = Optional.of(new String("Toy1"));    // of null 허용하지 않음 

   Optional<String> os2 = Optional.ofNullable(new String("Toy2"));    // ofNullable null 허용

 

   if(os1.isPresent())

      System.out.println(os1.get());

 

   if(os2.isPresent())

      System.out.println(os2.get());

}


Toy1

Toy2









Optional 클래스의 기본적인 사용 방법 2


class StringOptional2 {

   public static void main(String[] args) {

      Optional<String> os1 = Optional.of(new String("Toy1"));

      Optional<String> os2 = Optional.ofNullable(new String("Toy2"));

      os1.ifPresent(s -> System.out.println(s));  // 람다식 버전

      os2.ifPresent(System.out::println);  // 메소드 참조 버전

   }

}


public void ifPresent(Consumer<? super T> consumer)

 

   // Consumer<T>      void accept(T t)



Toy1

Toy2








Optional 클래스를 사용하면 if ~ else문을 대신할  있다:

이러한 if ~ else 문들


class ContInfo {

   String phone;  // null   있음 (가정)

   String adrs;  // null   있음 (가정)

 

   public ContInfo(String ph, String ad) {

      phone = ph;

      adrs = ad;

   }

   public String getPhone() { return phone; }

   public String getAdrs() { return adrs; }

}



public static void main(String[] args) {

   ContInfo ci = new ContInfo(null, "Republic of Korea");

   String phone;

   String addr;

   

   if(ci.phone != null)

      phone = ci.getPhone();

   else

      phone = "There is no phone number.";

 

   if(ci.adrs != null)

      addr = ci.getAdrs();

   else

      addr = "There is no address.";

 

   System.out.println(phone);

   System.out.println(addr);

}


There is no phone number.

Republic of Korea









Optional 클래스를 사용하면 if ~ else문을 대신할  있다:

map 메소드의 소개


public static void main(String[] args) {

   Optional<String> os1 = Optional.of("Optional String");

   Optional<String> os2 = os1.map(s -> s.toUpperCase());

   System.out.println(os2.get());

 

   Optional<String> os3 = os1.map(s -> s.replace(' ', '_'))

                             .map(s -> s.toLowerCase());

   System.out.println(os3.get());

}

 

// public <U> Optional<U> map(Function<? super T, ? extends U> mapper)

// Function<T, U> U apply(T t)


map 메소드는 apply 메소드가 반환하는 대상을 Optional 인스턴스에 담아서 반환한다.



OPTIONAL STRING

optional_string








Optional 클래스를 사용하면 if ~ else문을 대신할  있다:

orElse 메소드의 소개


public static void main(String[] args) {

   Optional<String> os1 = Optional.empty();

   Optional<String> os2 = Optional.of("So Basic");

 

   String s1 = os1.map(s -> s.toString())

                  .orElse("Empty");

 

   String s2 = os2.map(s -> s.toString())

                  .orElse("Empty");

 

   System.out.println(s1);

   System.out.println(s2);

}

Optional 인스턴스를 대상으로 orElse 메소드를 호출하면,

           orElse 호출하면서 전달된 인스턴스가 대신 반환된다.


저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 값을 반환함.


Empty

So Basic








Optional 클래스를 사용하면 if ~ else문을 대신할  있다 :

           최종 결론


public static void main(String[] args) {

   ContInfo ci = new ContInfo(null, "Republic of Korea");

   String phone;

   String addr;

   

   if(ci.phone != null)

      phone = ci.getPhone();

   else

      phone = "There is no phone number.";

 

   if(ci.adrs != null)

      addr = ci.getAdrs();

   else

      addr = "There is no address.";

 

   System.out.println(phone);

   System.out.println(addr);

}


->


public static void main(String[] args) {

   Optional<ContInfo> ci

     = Optional.of(new ContInfo(null, "Republic of Korea"));

 

   String phone = ci.map(c -> c.getPhone())

                    .orElse("There is no phone number.");

   String addr = ci.map(c -> c.getAdrs())

                   .orElse("There is no address.");

 

   System.out.println(phone);

   System.out.println(addr);

}








showCompAddr 메소드의 개선 결과

// public <U> Optional<U> map(Function<? super T, ? extends U> mapper)

// Function<T, U> U apply(T t)


public static void showCompAddr(Friend f) {

   String addr = null;

   if(f != null) {

      Company com = f.getCmp();

     

      if(com != null) {

         ContInfo info = com.getCInfo();

     

         if(info != null)

            addr = info.getAdrs();

      }

   }

   

   if(addr != null)

      System.out.println(addr);

   else

      System.out.println("There's no...");

}


->


public static void showCompAddr(Optional<Friend> f) {

   String addr = f.map(Friend::getCmp)

                  .map(Company::getCInfo)

                  .map(ContInfo::getAdrs)

                  .orElse("There's no...");

 

   System.out.println(addr);

}

 

public static void main(String[] args) {

   ContInfo ci =

       new ContInfo("321-444-577", "Republic of Korea");

   Company cp = new Company("YaHo Co., Ltd.", ci);

   Friend frn = new Friend("LEE SU", cp);

   showCompAddr(Optional.of(frn));

}









Optional 클래스의 flatMap 메소드:

Optional 클래스를 코드 전반에 사용하기 1


public static void main(String[] args) {

   Optional<String> os1 = Optional.of("Optional String");

   Optional<String> os2 = os1.map(s -> s.toUpperCase());

   System.out.println(os2.get());

   

   Optional<String> os3 = os1.flatMap(s -> Optional.of(s.toLowerCase()));

   System.out.println(os3.get());

}


map 람다식이 반환하는 내용물을 Optional 감싸서 반환!

flatMap 그냥 반환! 따라서 필요하면 직접 Optional 감싸야 한다.


OPTIONAL STRING

optional string








Optional 클래스의 flatMap 메소드:

Optional 클래스를 코드 전반에 사용하기 2


class ContInfo {

   Optional<String> phone;  // null   있음

   Optional<String> adrs;  // null   있음

 

   public ContInfo(Optional<String> ph, Optional<String> ad) {

      phone = ph;

      adrs = ad;

   }

   public Optional<String> getPhone() {

      return phone;

   }

   public Optional<String> getAdrs() {

      return adrs;

   }

}


public static void main(String[] args) {

   Optional<ContInfo> ci = Optional.of(

      new ContInfo(Optional.ofNullable(null), Optional.of("Korea"))

   );

 

   String phone = ci.flatMap(c -> c.getPhone())

                    .orElse("There is no phone number.");

   String addr = ci.flatMap(c -> c.getAdrs())

                   .orElse("There is no address.");

   System.out.println(phone);

   System.out.println(addr);

}


There is no phone number.

Republic of Korea








Optional 클래스의 flatMap 메소드:

Optional 클래스를 코드 전반에 사용하기 3


예제 NullPointerCaseStudy3.java 

               Optional 사용의 모델로 제공을 하니 적절히 참고하기 바랍니다.



반응형