람다메서드
메서드 참조
::
문법을 통해 메서드 참조가 가능하다.
스태틱 메서드
boolean isReal = list.stream().anyMatch(u -> User.isRealUser(u));
//이렇게
boolean isReal = list.stream().anyMatch(User::isRealUser);
object의 메서드
User user = new User();
boolean isLegalName = list.stream().anyMatch(user::isLegalName);
type의 메서드
long count = list.stream().filter(String::isEmpty).count();
생성자
Stream<User> stream = list.stream().map(User::new);
public class MethodReference {
List<String> withoutMethodReference =
cars.stream().map(car -> car.toString())
.collect(Collectors.toList());
}
//이렇게 변경
public class MethodReference {
List<String> methodReference = cars.stream().map(Car::toString)
.collect(Collectors.toList());
}
디폴트 메서드
인터페이스는 기본적으로 메서드 시그니처만 가지고 있어야 하며 구현부를 가지면 안된다.
인터페이스는 구현한 모든 클래스에서 모든 메서드를 구현해야 한다.
이 때문에, 나중에 특정 클래스에서만 사용할 메서드를 추가하려 하면 모든 구현 클래스도 동일하게 구현해줘야 하는 문제가 생긴다.
이 때 default 메서드를 사용하면 해당 인터페이스는 default 메서드를 기본적으로 구현부까지 가지고 있는 셈이기 때문에, 다른 구현 클래스에서 구현해주지 않아도 된다. 추후에 다른 구현 클래스에서 언제든 사용할 수 있으며 오버라이딩도 할 수 있다.
public class DefaultMethods {
public interface Logging {
void log(String message);
default void log(String message, Date date) {
System.out.println(date.toString() + ": " + message);
}
}
}
Optional<T>
NullPointerException(NPE) 을 더 가독성좋게 처리하는 방법
Optional<String> optional = Optional.empty();
String str = "value";
Optional<String> optional = Optional.of(str);
Optional<String> optional = Optional.ofNullable(getString());
List<String> list = getList();
List<String> listOpt = list != null ? list : new ArrayList<>();
List<String> listOpt = getList().orElseGet(() -> new ArrayList<>());
아래 코드에서 User가 사용하는 .map()
함수는 getAdress()
의 반환값을 Optional<Address>
로 바꾸고, getStreet()
의 반환값을 Optional<String>
으로 바꿔준다.
그래서 함수 반환값이 null일 경우 map() 함수는 비어있는 Optional을 반환한다.
User user = getUser();
if (user != null) {
Address address = user.getAddress();
if (address != null) {
String street = address.getStreet();
if (street != null) {
return street;
}
}
}
return "not specified";
Optional<User> user = Optional.ofNullable(getUser());
String result = user
.map(User::getAddress)
.map(Address::getStreet)
.orElse("not specified");
만약 함수가 Optional<T>
를 반환한다면, flatMap()
함수를 사용해야 한다.
Optional<OptionalUser> optionalUser = Optional.ofNullable(getOptionalUser());
String result = optionalUser
.flatMap(OptionalUser::getAddress)
.flatMap(OptionalAddress::getStreet)
.orElse("not specified");
NPE 처리 방법 개선
String value = null;
String result = "";
try {
result = value.toUpperCase();
} catch (NullPointerException exception) {
throw new CustomException();
}
//이렇게
String value = null;
Optional<String> valueOpt = Optional.ofNullable(value);
String result = valueOpt.orElseThrow(CustomException::new).toUpperCase();
타입 어노테이션
지역 변수 정의
public class TypeAnnotations {
public static void main(String[] args) {
@NotNull String userName = args[0];
}
}
생성자 호출
public class TypeAnnotations {
public static void main(String[] args) {
List<String> request =
new @NotEmpty ArrayList<>(Arrays.stream(args).collect(
Collectors.toList()));
}
}
타입 캐스팅
제네릭
public class TypeAnnotations {
public static void main(String[] args) {
List<@Email String> emails;
}
}
Try-with-resources
public class TryWithResources {
public static void main(String[] args) {
BufferedReader br = new BufferedReader(
new StringReader("Hello world example!"));
try {
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//이렇게
public class TryWithResources {
public static void main(String[] args) {
final BufferedReader br3 = new BufferedReader(
new StringReader("Hello world example3!"));
try (BufferedReader reader = br3) {
System.out.println(reader.readLine());
} catch (IOException e) {
System.out.println("Error happened!");
}
}
}
//또는
MyAutoCloseable mac = new MyAutoCloseable();
try (mac) {
// do some stuff with mac
}
try (new MyAutoCloseable() { }.finalWrapper.finalCloseable) {
// do some stuff with finalCloseable
} catch (Exception ex) { }
내부 익명 클래스에 다이아몬드 연산자 적용
<>() { some.. }
처럼 <>를 사용하여 컴파일러 타입 추론을 유도하는 기능은 익명 내부 클래스에서는 사용 불가했으나 가능해졌다.public class DiamondOperator {
StringAppender<String> appending = new StringAppender<>() {
@Override
public String append(String a, String b) {
return new StringBuilder(a).append("-").append(b).toString();
}
};
public abstract static class StringAppender<T> {
public abstract T append(String a, String b);
}
}
//또는
FooClass<Integer> fc = new FooClass<>(1) { // anonymous inner class
};
FooClass<? extends Integer> fc0 = new FooClass<>(1) {
// anonymous inner class
};
FooClass<?> fc1 = new FooClass<>(1) { // anonymous inner class
};
private interface method
public class PrivateInterfaceMethods {
public static void main(String[] args) {
TestingNames names = new TestingNames();
System.out.println(names.fetchInitialData());
}
public static class TestingNames implements NamesInterface {
public TestingNames() {
}
}
public interface NamesInterface {
default List<String> fetchInitialData() {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(this.getClass()
.getResourceAsStream("/names.txt")))) {
return readNames(br);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private List<String> readNames(BufferedReader br)
throws IOException {
ArrayList<String> names = new ArrayList<>();
String name;
while ((name = br.readLine()) != null) {
names.add(name);
}
return names;
}
}
}
//또는
interface InterfaceWithPrivateMethods {
private static String staticPrivate() {
return "static private";
}
private String instancePrivate() {
return "instance private";
}
default void check() {
String result = staticPrivate();
InterfaceWithPrivateMethods pvt = new InterfaceWithPrivateMethods() {
// anonymous class
};
result = pvt.instancePrivate();
}
}}
불변 Set
Set.of() 를 통해 불변 set 객체를 생성할 수 있다.
따라서 생성, 추가 등 조작을 가할 경우 UnsupportedOperationException 이 발생한다.
Set<String> strKeySet = Set.of("key1", "key2", "key3");
Stream에 Optional 적용 가능
List<String> filteredList = listOfOptionals.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
Multi Resolution Image API
한 개 객체로 여러 해상도를 지원하기위한 API
BufferedImage[] resolutionVariants = ....
MultiResolutionImage bmrImage
= new BaseMultiResolutionImage(baseIndex, resolutionVariants);
Image testRVImage = bmrImage.getResolutionVariant(16, 16);
assertSame("Images should be the same", testRVImage, resolutionVariants[3]);