Spring Framework에서 제공하는 기능이다.
코드에서 처럼 Object를 참조한 person은 getter, setter가 있어도 접근 할 수 없다.
reflection을 사용하여 class를 가져온 후 field를 찾는다.
여기서 field를 조작할 수 도 있다.
spring framework에서 bean factory에 등록한 bean을 관리하는 것도 reflection을 활용 한 것이다.
public class ReflectionExample {
static class Person {
private String name;
private int age;
}
public static void main(String[] args) {
givenObject_whenGetsFieldNamesAtRuntime_thenCorrect();
}
public static void givenObject_whenGetsFieldNamesAtRuntime_thenCorrect() {
// All object(ex -> Person) are sub-class of Object
Object person = new Person();
// Now using reflect to find Person's fields.
// Returns the runtime class of this Object.
// And returns an array of Field objects reflecting all the fields declared by the class or interface represented by this Class object.
Field[] fields = person.getClass().getDeclaredFields();
List<String> actualFieldNames = getFieldNames(fields);
// List extends Collection extends Iterable.forEach(Consumer<? super T> action)
// Using MessageFormat for thread-safe
actualFieldNames.forEach(s -> System.out.println(MessageFormat.format("{0}", s)));
}
private static List<String> getFieldNames(Field[] fields) {
List<String> fieldNames = new ArrayList<>();
for (Field field : fields) {
fieldNames.add(field.getName());
}
return fieldNames;
}
}
아래 코드는 다른 thread가 접근하여도 똑같은 instance를 받는다.
public class Test {
private static final Test INSTANCE = new Test();
private Test() { ... }
public static Test getInstance() { return INSTANCE; }
...
}
아래 코드는 thread 별로 다른 instance를 받는다.
public class Test {
private Test() { ... }
public static Test getInstance() { return new Test(); }
...
}
Generic Type을 사용하는 singleton factory다.
public class GenericSingletonFactory {
private static class SetFactory {
private static final Set EMPTY_SET = new HashSet();
private static <T> Set<T> emptySet() {
return (Set<T>) EMPTY_SET;
}
}
public static void main(String[] args) {
Set<String> stringSet = SetFactory.emptySet();
stringSet.add("hello");
System.out.println(stringSet);
}
}
static factory method의 method reference를 supplier로 사용 할 수 있다.
supplier는 functional이기 때문에 lazy하게 처리 할 수 있다.
// Supplier 코드
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
// Supplier를 사용한 random한 숫자 받기
public class SupplierTest {
public static void main(String[] args) {
Supplier<Double> randomValue = new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
};
Supplier<Double> randomValue2 = () -> Math.random();
Supplier<Double> randomValue3 = Math::random;
System.out.println(randomValue.get());
}
}
// Supplier를 반환하는 static method factory
public class CarFactory {
private static Map<String, Supplier<Car>> cars;
static {
cars = new HashMap<>();
cars.put("Kia", KiaCar::new);
cars.put("Hyundai", HyundaiCar::new);
}
public static Car create(final String carName) {
try {
return cars.get(carName).get();
} catch (NullPointerException e) {
throw new IllegalArgumentException("해당하는 차가 없습니다!");
}
}
}
Serialization한 class를 Deserialization하면 Signleton이 부셔진다!
여기참고
public class Test {
private static final Test INSTANCE = new Test();
private Test() { ... }
public static Test getInstance() { return INSTANCE; }
// Deserialization 막기!
private Object readResolve() {
// 진짜 Test를 반환하고 가짜 Test는 GC에 맡긴다.
return INSTANCE;
}
}
원소가 하나 뿐인 enum을 쓰는게 singleton을 하기엔 가장 좋다.
하지만! enum 외에 다른 것을 상속해야 한다면 다른 방법이 좋다.
그런데, enum에서 interface를 구현하는 방법은 가능하다.
public enum Test implements Test2 {
INSTANCE;
@Override
public void a() {
}
}
public static interface Test2 {
void a();
}