public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
BigInteger(int, int, Random)
에 비해BigInteger.probablePrime
이 더 직관적이다.Constructor(int i, String s)
와Constructor(String s, int i)
는 서로 다르다는 점을 이용하는 방식반환 객체의 클래스를 선택 가능한 유연성
Java8이전의 경우 인터페이스에서 정적 메서드 반환 불가
public interface Type {
...
}
이를 반환하는 정적 메서드typeFactory()
Type 내에 생성이 불가능하기에
public class Types {
static Type typeFactory() {
...
}
}
위와 같은 방식으로 동반 클래스(companion class)를 만들어야 했다.
자바8 이후로는 인터페이스가 정적 메서드를 가질 수 있다.
java.util.EnumSet.java
의 경우/**
* Creates an empty enum set with the specified element type.
*
* @param <E> The class of the elements in the set
* @param elementType the class object of the element type for this enum
* set
* @return An empty enum set of the specified type.
* @throws NullPointerException if {@code elementType} is null
*/
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum<?>[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
}
원소의 수에 따라 RegularEnumSet
이나 JumboEnumSet
을 반환한다.
하위 클래스이기만 한다면
클라이언트가 하위 클래스의 존재를 모르며,
필요 없는 하위 클래스는 삭제해도 문제가 없으며
필요한 다른 클래스를 추가해도 된다는 점에 주목하라.
서비스 제공자 프레임워크(service provider framework)
대표적인 예로 JDBC(Java Database Connectivity)
제공자(provider)인 서비스 구현체들을 클라이언트에 제공하는 역할을 프레임워크가 담당 -> 클라이언트를 구현체로부터 분리!
구성: 3개의 핵심 컴포넌트(경우에 따라 +1)
서비스 인터페이스(service inteface): 구현체의 동작을 정의
제공자 등록 API(provider registration API): 제공자가 구현체를 등록할 때 사용
서비스 접근 API(service access API): 클라이언트가 서비스의 인스턴스를 얻을 때 사용
클라이언트가 원하는 구현체의 조건을 명시하여 반환받음, 명시하지 않으면 기본 구현체 혹은 지원 구현체 중 하나를 반환
```java
@CallerSensitive
public static Connection getConnection(String url,
java.util.Properties info) throws SQLException {
return (getConnection(url, info, Reflection.getCallerClass()));
}
...
@CallerSensitive
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
...
@CallerSensitive
public static Connection getConnection(String url)
throws SQLException {
java.util.Properties info = new java.util.Properties();
return (getConnection(url, info, Reflection.getCallerClass()));
}
...
// Worker method called by the public getConnection() methods.
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
if (callerCL == null || callerCL == ClassLoader.getPlatformClassLoader()) {
callerCL = Thread.currentThread().getContextClassLoader();
}
if (url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
ensureDriversInitialized();
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
for (DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if (isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.driver.getClass().getName());
}
}
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
제공된 파라미터에 따라서 실제로 getConnection() 작업을 하는 메서드에 형식을 맞춰 파라미터를 넘기고, 인터페이스로서 Driver를 받고 있다.
그런데 왜 리플렉션을 사용할까?
서비스 제공자 인터페이스(service provider interface): 서비스 인터페이스의 인스턴스를 생성하는 팩터리 객체를 설명
리플렉션?
서비스 제공자 프레임워크 패턴의 변형
브리지 패턴: 서비스 접근 API가 공급자가 제공하는 것보다 더 풍부한 서비스 인터페이스를 클라이언트에 반환
DI 프레임워크
java,util.ServiceLoader
: 자바6부터 제공되는 범용 서비스 제공자 프레임워크Date d = Date.from(instant);
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING)
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
StackWalker luke = StackWalker.getInstance(options);
Object newArray = Array.newInstance(classObject, arrayLen);
FileStore fs = Files.getFileStore(path);
BufferedReader br = Files.newBufferedReader(path);
List<Complaint> litany = Collections.list(legacyLitany);
핵심 정리
정적 팩터리 메서드와 public 생성자는 각자의 쓰임새가 있으니 상대적인 장단점을 이해하고 사용하는 것이 좋다. 그렇다고 하더라도 정적 팩터리를 사용하는 게 유리한 경우가 많으므로 무작정 public 생성자를 제공하던 습관이 있다면 고치자.
장점 5번째에서 예제 코드를 올려주셔서 이해하는데 더 도움이 된 것 같습니다 ! ㅎㅎ